Remove DEBUG lines.
[userinfo.git] / src / modules / login.c
blob7c3449dafa9c0b8ba231e3ae276872618d186131
1 /*
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
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <pwd.h>
30 #include <ctype.h>
32 #ifndef HAVE_STRSEP
33 #include "../strsep.c"
34 #endif
36 #ifndef HAVE_ERR_H
37 #include "../err.c"
38 #endif
40 #include "login.h"
41 #include "common.h"
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;
49 static int lastlogfd;
50 static time_t now;
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)
58 *chainable = 0;
59 time(&now);
62 void ui_module_exit()
64 #ifdef HAVE_PROCFS
65 if (procdir)
66 closedir(procdir);
67 #endif
69 #ifdef HAVE_KVM_H
70 if (kd)
71 kvm_close(kd);
72 #endif
74 if (lastlogfd)
75 close(lastlogfd);
78 #ifndef HAVE_UTMPX_H
79 /* This is for *BSD (login process id). */
80 #ifdef BSD_KVM
81 static char *get_pid(uid_t uid, int multi)
83 static int firstrun;
84 static char line[LINE_MAX];
85 int cnt, i;
86 pid_t pid = 0;
87 char errbuf[LINE_MAX];
88 struct kinfo_proc *kp;
90 line[0] = '\0';
92 if (!kd && firstrun)
93 return "!";
95 if (!kd) {
96 firstrun = 1;
98 #ifdef __NetBSD__
99 if ((kd = kvm_openfiles(NULL, NULL, NULL,
100 #else
101 if ((kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL,
102 #endif
103 O_RDONLY, errbuf)) == NULL) {
104 warnx("%s", errbuf);
105 return "!";
109 if ((kp = kvm_getprocs(kd, KERN_PROC_UID, uid, &cnt)) == NULL) {
110 warnx("kvm_getprocs(): %s", kvm_geterr(kd));
111 return "!";
114 for (i = 0; i < cnt; i++) {
115 char buf[32];
117 #if __FreeBSD_version < 500000
118 if (kp[i].kp_eproc.e_flag & EPROC_SLEADER && kp[i].kp_eproc.e_tdev !=
119 -1) {
120 pid = kp[i].kp_eproc.e_ppid;
122 * pid = kp[i].kp_proc.p_pid;
125 if (pid == 1)
126 continue;
127 #else
128 if (kp[i].ki_kiflag & KI_SLEADER && kp[i].ki_tdev != -1) {
129 pid = kp[i].ki_pid;
130 #endif
131 snprintf(buf, sizeof(buf), "%i%c", pid, multi);
132 safe_strncat(line, buf, sizeof(line));
136 if (line[0] == '\0')
137 return "!";
139 line[strlen(line) - 1] = '\0';
140 return line;
143 /* This is for Linux and Solaris. */
144 #elif defined(HAVE_PROCFS)
145 #include <sys/types.h>
146 #include <sys/stat.h>
148 #ifdef HAVE_DIRENT_H
149 #include <dirent.h>
150 #endif
152 #ifdef __svr4__
153 #include <unistd.h>
154 #include <procfs.h>
155 #endif
157 static char *get_pid(uid_t uid, int multi)
159 static int firstrun;
160 struct dirent *ent;
161 struct stat st;
162 static char line[LINE_MAX];
163 pid_t *pids = 0;
164 int pid_index = 0;
166 #ifdef __svr4__
167 int fd;
168 struct pstatus pstat;
169 #else
170 FILE *fp;
171 #endif
173 line[0] = '\0';
175 if (!procdir && firstrun)
176 return "!";
178 if (!procdir) {
179 firstrun = 1;
181 if ((procdir = opendir("/proc")) == NULL) {
182 warn("%s", "/proc");
183 return "!";
187 rewinddir(procdir);
189 again:
190 while ((ent = readdir(procdir)) != NULL) {
191 pid_t pid = -1;
192 char filename[FILENAME_MAX];
193 char buf[LINE_MAX];
194 int i;
196 #ifndef __svr4__
197 char *t;
198 #endif
200 if (!isdigit((unsigned char) *ent->d_name))
201 continue;
203 #ifdef __linux__
204 snprintf(filename, sizeof(filename), "/proc/%s/stat", ent->d_name);
205 #else
206 snprintf(filename, sizeof(filename), "/proc/%s/status", ent->d_name);
207 #endif
209 if (stat(filename, &st) == -1)
210 continue;
213 * The current user owns this file (process id).
215 if (st.st_uid == uid) {
216 #ifdef __svr4__
217 if ((fd = open(filename, O_RDONLY)) == -1)
218 continue;
220 if (pread(fd, &pstat, sizeof(struct pstatus), 0) !=
221 sizeof(struct pstatus)) {
222 close(fd);
223 continue;
226 pid = pstat.pr_ppid;
227 close(fd);
228 #else
229 if ((fp = fopen(filename, "r")) == NULL)
230 continue;
232 if ((t = fgets(buf, sizeof(buf), fp)) == NULL) {
233 fclose(fp);
234 continue;
237 #ifdef __linux__
238 if ((i = sscanf(buf, "%*i %*s %*c %*i %*i %i", &pid)) < 1) {
239 #endif
241 #else
242 if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) {
243 #endif
245 fclose(fp);
246 continue;
249 fclose(fp);
250 #endif
253 * Skip duplicate pids.
255 for (i = 0; i < pid_index; i++) {
256 if (pids[i] == pid)
257 goto again;
260 snprintf(buf, sizeof(buf), "%li%c", (unsigned long) pid, multi);
261 safe_strncat(line, buf, sizeof(line));
263 if ((pids =
264 realloc(pids, (pid_index + 2) * sizeof(pid_t *))) == NULL) {
265 warn("realloc()");
266 continue;
269 pids[pid_index++] = pid;
273 if (pid_index)
274 free(pids);
276 if (line[0] == '\0')
277 return "!";
279 line[strlen(line) - 1] = '\0';
280 return line;
282 #else
283 /* Unsupported OS. */
284 static char *get_pid(uid_t uid, int multi)
286 return "!";
288 #endif
289 #endif
291 /* Break up the last login string into sections and add the sections to the
292 * output string array if needed. */
293 static void last_strings(char *str)
295 int i = 0;
296 char *buf;
297 const char *line, *host, *when;
299 line = host = when = (str) ? "-" : "!";
301 while ((buf = strsep(&str, ",")) != NULL) {
302 if (!buf[0])
303 continue;
305 switch (i++) {
306 case 0:
307 line = buf;
308 break;
309 case 1:
310 host = buf;
311 break;
312 case 2:
313 when = buf;
314 break;
315 default:
316 break;
320 for (i = 0; i < strlen(last_options); i++) {
321 switch (last_options[i]) {
322 case 'y':
323 add_string(&strings, line);
324 break;
325 case 'h':
326 add_string(&strings, host);
327 break;
328 case 't':
329 add_string(&strings, when);
330 break;
331 case 'a':
332 add_string(&strings, line);
333 add_string(&strings, host);
334 add_string(&strings, when);
335 default:
336 break;
341 /* Get the lastlog structure from the lastlog file. */
342 #if (defined(__FreeBSD_version) && __FreeBSD_version >= 900000)
343 static char *lastlogin(const struct passwd *pw, char *tf)
345 struct utmpx *last;
346 static char buf[LINE_MAX];
348 if (setutxdb(UTXDB_LASTLOGIN, NULL) == -1) {
349 warn("lastlog");
350 return NULL;
353 last = getutxuser(pw->pw_name);
355 if (!last)
356 return NULL;
358 snprintf(buf, sizeof(buf), "%s,%s,%s",
359 !last->ut_line[0] ? "!" : last->ut_line,
360 (!last->ut_host[0] || !isalnum(last->ut_host[0])) ?
361 !isdigit(last->ut_line[3]) ? "!" : "-" : last->ut_host,
362 !last->ut_time[0] ? "!" : stamp(last->ut_time, tf));
364 return buf;
366 #else
367 static char *lastlogin(const struct passwd *pw, char *tf)
369 int count;
370 long offset;
371 static char buf[LINE_MAX];
372 struct lastlog last;
374 if (lastlogfd < 0)
375 return NULL;
377 if (!lastlogfd) {
378 if ((lastlogfd = open(_PATH_LASTLOG, O_RDONLY)) == -1) {
379 warn("%s", _PATH_LASTLOG);
380 return NULL;
384 offset = (long) pw->pw_uid * sizeof(struct lastlog);
386 if (lseek(lastlogfd, offset, SEEK_SET) == -1) {
387 warn("%s", _PATH_LASTLOG);
388 return NULL;
391 if ((count = read(lastlogfd, &last, sizeof(struct lastlog))) !=
392 sizeof(struct lastlog)) {
393 if (count == -1)
394 warn("%s", _PATH_LASTLOG);
396 return NULL;
399 #ifdef __NetBSD__
400 #ifdef HAVE_UTMPX_H
401 last.ll_host[UTX_HOSTSIZE-1] = '\0';
402 last.ll_line[UTX_LINESIZE-1] = '\0';
403 #else
404 last.ll_host[UT_HOSTSIZE-1] = '\0';
405 last.ll_line[UT_LINESIZE-1] = '\0';
406 #endif
407 #else
408 last.ll_host[UT_HOSTSIZE-1] = '\0';
409 last.ll_line[UT_LINESIZE-1] = '\0';
410 #endif
412 snprintf(buf, sizeof(buf), "%s,%s,%s",
413 !last.ll_line[0] ? "!" : last.ll_line,
414 (!last.ll_host[0] || !isalnum(last.ll_host[0])) ?
415 !isdigit(last.ll_line[3]) ? "!" : "-" : last.ll_host,
416 !last.ll_time ? "!" : stamp(last.ll_time, tf));
417 return buf;
419 #endif
421 /* This will return an array of utmp structures if a user is logged in, NULL
422 * otherwise. We'll try to keep the utmp file descriptor open if possible to
423 * speed things up a bit. */
424 static UTMP **get_utmp(const char *user)
426 UTMP **logins = NULL;
427 #ifdef HAVE_UTMPX_H
428 UTMP *u;
429 #else
430 UTMP u;
431 int count;
432 static int fd;
434 if (fd < 0)
435 return NULL;
437 if (!fd) {
438 if ((fd = open(_PATH_UTMP, O_RDONLY)) == -1) {
439 warn("%s", _PATH_UTMP);
440 return NULL;
443 #endif
445 login_count = 0;
447 #ifdef HAVE_UTMPX_H
448 setutxent();
450 while ((u = getutxent()) != NULL) {
451 if (!strcmp(u->ut_user, user)) {
452 #else
453 lseek(fd, 0, SEEK_SET);
455 while ((count = read(fd, &u, sizeof(UTMP))) == sizeof(UTMP)) {
456 if (strcmp(u.ut_name, user) == 0) {
457 #endif
458 if ((logins = realloc(logins,
459 (login_count + 2) * sizeof(UTMP *))) ==
460 NULL) {
461 warn("realloc()");
462 return NULL;
465 if ((logins[login_count] = malloc(sizeof(UTMP))) == NULL) {
466 warn("malloc()");
467 return NULL;
470 #ifdef HAVE_UTMPX_H
471 #ifdef __NetBSD__
472 memcpy(logins[login_count]->ut_name, u->ut_name, UTX_NAMESIZE);
473 logins[login_count]->ut_name[UTX_NAMESIZE-1] = 0;
474 memcpy(logins[login_count]->ut_line, u->ut_line, UTX_LINESIZE);
475 logins[login_count]->ut_line[UTX_LINESIZE-1] = 0;
476 memcpy(logins[login_count]->ut_host, u->ut_host, UTX_HOSTSIZE);
477 logins[login_count]->ut_host[UTX_HOSTSIZE-1] = 0;
478 logins[login_count]->ut_pid = u->ut_pid;
479 #else
480 memcpy(logins[login_count]->ut_user, u->ut_user, UT_NAMESIZE);
481 logins[login_count]->ut_user[UT_NAMESIZE-1] = 0;
482 memcpy(logins[login_count]->ut_line, u->ut_line, UT_LINESIZE);
483 logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
484 memcpy(logins[login_count]->ut_host, u->ut_host, UT_HOSTSIZE);
485 logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
486 logins[login_count]->ut_tv.tv_sec = u->ut_tv.tv_sec;
487 logins[login_count]->ut_pid = u->ut_pid;
488 #endif
489 #else
490 memcpy(logins[login_count]->ut_name, u.ut_name, UT_NAMESIZE);
491 logins[login_count]->ut_name[UT_NAMESIZE-1] = 0;
492 memcpy(logins[login_count]->ut_line, u.ut_line, UT_LINESIZE);
493 logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
494 memcpy(logins[login_count]->ut_host, u.ut_host, UT_HOSTSIZE);
495 logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
496 logins[login_count]->ut_time = u.ut_time;
497 #endif
498 logins[++login_count] = NULL;
502 return logins;
505 /* The 'mesg' status of the logged in user. */
506 static char *msgstat(UTMP **u, int multi)
508 static char line[LINE_MAX];
509 int i;
511 line[0] = '\0';
513 for (i = 0; i < login_count; i++) {
514 char filename[FILENAME_MAX];
515 struct stat st;
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 safe_strncat(line, "!", sizeof(line));
522 else
523 safe_strncat(line,
524 (st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) ? "1" : "0",
525 sizeof(line));
527 safe_strncat(line, m, sizeof(line));
530 if (line[0] == '\0')
531 return "!";
533 line[strlen(line) - 1] = '\0';
534 return line;
537 /* Returns the users idle time in seconds. */
538 static char *idle(UTMP **u, int multi)
540 static char line[LINE_MAX];
541 time_t t;
542 struct stat st;
543 int i;
545 line[0] = '\0';
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 safe_strncat(line, "!", sizeof(line));
555 safe_strncat(line, m, sizeof(line));
556 continue;
559 #ifdef HAVE_UTMPX_H
560 if (u[i]->ut_tv.tv_sec > st.st_atime) {
561 #else
562 if (u[i]->ut_time > st.st_atime) {
563 #endif
564 safe_strncat(line, "-", sizeof(line));
565 safe_strncat(line, m, sizeof(line));
566 continue;
569 t = st.st_atime;
571 #ifdef HAVE_UTMPX_H
572 if (t < u[i]->ut_tv.tv_sec)
573 t = u[i]->ut_tv.tv_sec;
574 #else
575 if (t < u[i]->ut_time)
576 t = u[i]->ut_time;
577 #endif
579 snprintf(buf, sizeof(buf), "%lu", (now - t <= 0) ? 0 : now - t);
580 safe_strncat(line, buf, sizeof(line));
581 safe_strncat(line, m, sizeof(line));
584 if (line[0] == '\0')
585 return "!";
587 line[strlen(line) - 1] = '\0';
588 return line;
591 /* This is output if the -h command line option is passed to the main program.
593 void ui_module_help()
595 printf(" Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER);
596 printf("\t-y tty\t\t\t\t");
597 printf("-m message status\n");
598 printf("\t-t login time stamp\t\t");
599 printf("-d duration in minutes\n");
600 printf("\t-h hostname\t\t\t");
601 printf("-i seconds idle\n");
602 printf("\t-p login process id\n");
603 printf("\t-l lastlog information"
604 " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n");
605 return;
608 /* This is the equivalent to main() only without argc and argv available. */
609 int ui_module_exec(char ***s, const struct passwd *pw, const int multi,
610 const int verbose, char *tf)
612 char *p = options;
613 UTMP **u = NULL, **up;
614 char buf[255];
616 login_count = 0;
617 u = get_utmp(pw->pw_name);
618 strings = *s;
620 while (*p) {
621 char line[LINE_MAX] = { '\0' };
622 int i;
623 char m[2] = { multi, '\0' };
625 switch (*p) {
626 case 'i':
627 add_string(&strings, (u) ? idle(u, multi) : "!");
628 break;
629 case 'l':
630 last_strings(lastlogin(pw, tf));
631 break;
632 case 'h':
633 for (i = 0; i < login_count; i++) {
634 if (u[i]->ut_host[0]
635 && isalnum((unsigned char) u[i]->ut_host[0])) {
636 safe_strncat(line, u[i]->ut_host, sizeof(line));
638 else {
640 * If a users tty is tty1-n, it must be a console
641 * * login.
643 if (u[i]->ut_line[0]
644 && isdigit((unsigned char) u[i]->ut_line[3]))
645 safe_strncat(line, "-", sizeof(line));
646 else
647 safe_strncat(line, "!", sizeof(line));
650 safe_strncat(line, m, sizeof(line));
653 if (line[0] == '\0')
654 strncpy(line, "!", sizeof(line));
655 else
656 line[strlen(line) - 1] = '\0';
658 add_string(&strings, line);
659 break;
660 case 'y':
661 for (i = 0; i < login_count; i++) {
662 if (u[i]->ut_line[0])
663 safe_strncat(line, u[i]->ut_line, sizeof(line));
664 else
665 safe_strncat(line, "!", sizeof(line));
667 safe_strncat(line, m, sizeof(line));
670 if (line[0] == '\0')
671 strncpy(line, "!", sizeof(line));
672 else
673 line[strlen(line) - 1] = '\0';
675 add_string(&strings, line);
676 break;
677 case 'm':
678 add_string(&strings, msgstat(u, multi));
679 break;
680 case 't':
681 for (i = 0; i < login_count; i++) {
682 #ifdef HAVE_UTMPX_H
683 safe_strncat(line, stamp(u[i]->ut_tv.tv_sec, tf), sizeof(line));
684 #else
685 safe_strncat(line, stamp(u[i]->ut_time, tf), sizeof(line));
686 #endif
687 safe_strncat(line, m, sizeof(line));
690 if (line[0] == '\0')
691 strncpy(line, "!", sizeof(line));
692 else
693 line[strlen(line) - 1] = '\0';
695 add_string(&strings, line);
696 break;
697 case 'd':
698 for (i = 0; i < login_count; i++) {
699 #ifdef HAVE_UTMPX_H
700 if ((now - u[i]->ut_tv.tv_sec) > 60) {
701 snprintf(buf, sizeof(buf), "%lu",
702 ((now - u[i]->ut_tv.tv_sec) / 60));
703 #else
704 if ((now - u[i]->ut_time) > 60) {
705 snprintf(buf, sizeof(buf), "%lu",
706 ((now - u[i]->ut_time) / 60));
707 #endif
708 safe_strncat(line, buf, sizeof(line));
710 else
711 safe_strncat(line, "-", sizeof(line));
713 safe_strncat(line, m, sizeof(line));
716 if (line[0] == '\0')
717 strncpy(line, "!", sizeof(line));
718 else
719 line[strlen(line) - 1] = '\0';
721 add_string(&strings, line);
722 break;
723 case 'p':
724 #ifdef HAVE_UTMPX_H
725 for (i = 0; i < login_count; i++) {
726 if (u[i]->ut_pid) {
727 snprintf(buf, sizeof(buf), "%li", (long) u[i]->ut_pid);
728 safe_strncat(line, buf, sizeof(line));
730 else
731 safe_strncat(line, "!", sizeof(line));
733 safe_strncat(line, m, sizeof(line));
736 if (line[0] == '\0')
737 strncpy(line, "!", sizeof(line));
738 else
739 line[strlen(line) - 1] = '\0';
741 add_string(&strings, line);
742 #else
743 add_string(&strings, (u) ? get_pid(pw->pw_uid, multi) : "!");
744 #endif
745 break;
746 default:
747 break;
750 p++;
753 if (login_count) {
754 for (up = u; *up; up++)
755 free(*up);
757 free(u);
760 *s = strings;
761 return EXIT_SUCCESS;
764 /* See if the last login options (-l) are valid. */
765 static int parse_last_options(const char *args)
767 int i = 0;
769 for (i = 0; i < strlen(args); i++) {
770 switch (args[i]) {
771 case 'y':
772 case 'h':
773 case 't':
774 case 'a':
775 break;
776 default:
777 return 1;
781 return 0;
784 char *ui_module_options_init(char **defaults)
786 *defaults = "L";
787 return LOGIN_OPTION_STRING;
790 /* Check module option validity. */
791 int ui_module_options(int argc, char **argv)
793 int opt;
794 char *p = options;
796 while ((opt = getopt(argc, argv, LOGIN_OPTION_STRING)) != -1) {
797 switch (opt) {
798 case 'l':
799 if (parse_last_options(optarg))
800 return 1;
802 last_options = optarg;
803 break;
804 case 'L':
805 strncpy(options, LOGIN_OPTION_ORDER, sizeof(options));
806 last_options = "a";
807 return 0;
808 case 'p':
809 case 'd':
810 case 'i':
811 case 'm':
812 case 'y':
813 case 'h':
814 case 't':
815 break;
816 case '?':
817 warnx("login: invalid option -- %c", optopt);
818 default:
819 return 1;
822 *p++ = opt;
823 *p = '\0';
826 return 0;