cppcheck fixes.
[userinfo.git] / src / modules / login.c
bloba046e7a574d1c6797c7e55ef2a77c56362d4f548
1 /*
2 Copyright (C) 2001-2011 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 02110-1301 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 static void free_logins(UTMP **u)
80 if (login_count) {
81 UTMP **up;
83 for (up = u; *up; up++)
84 free(*up);
86 free(u);
90 #ifndef HAVE_UTMPX_H
91 /* This is for *BSD (login process id). */
92 #ifdef BSD_KVM
93 static char *get_pid(uid_t uid, int multi)
95 static int firstrun;
96 static char line[LINE_MAX];
97 int cnt, i;
98 pid_t pid = 0;
99 char errbuf[LINE_MAX];
100 struct kinfo_proc *kp;
102 line[0] = '\0';
104 if (!kd && firstrun)
105 return "!";
107 if (!kd) {
108 firstrun = 1;
110 #ifdef __NetBSD__
111 if ((kd = kvm_openfiles(NULL, NULL, NULL,
112 O_RDONLY, errbuf)) == NULL) {
113 #else
114 if ((kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL,
115 O_RDONLY, errbuf)) == NULL) {
116 #endif
117 warnx("%s", errbuf);
118 return "!";
122 if ((kp = kvm_getprocs(kd, KERN_PROC_UID, uid, &cnt)) == NULL) {
123 warnx("kvm_getprocs(): %s", kvm_geterr(kd));
124 return "!";
127 for (i = 0; i < cnt; i++) {
128 char buf[32];
130 #if __FreeBSD_version < 500000
131 if (kp[i].kp_eproc.e_flag & EPROC_SLEADER && kp[i].kp_eproc.e_tdev !=
132 -1) {
133 pid = kp[i].kp_eproc.e_ppid;
135 * pid = kp[i].kp_proc.p_pid;
138 if (pid == 1)
139 continue;
140 #else
141 if (kp[i].ki_kiflag & KI_SLEADER && kp[i].ki_tdev != -1) {
142 pid = kp[i].ki_pid;
143 #endif
144 snprintf(buf, sizeof(buf), "%i%c", pid, multi);
145 safe_strncat(line, buf, sizeof(line));
149 if (line[0] == '\0')
150 return "!";
152 line[strlen(line) - 1] = '\0';
153 return line;
156 /* This is for Linux and Solaris. */
157 #elif defined(HAVE_PROCFS)
158 #include <sys/types.h>
159 #include <sys/stat.h>
161 #ifdef HAVE_DIRENT_H
162 #include <dirent.h>
163 #endif
165 #ifdef __svr4__
166 #include <unistd.h>
167 #include <procfs.h>
168 #endif
170 static char *get_pid(uid_t uid, int multi)
172 static int firstrun;
173 struct dirent *ent;
174 struct stat st;
175 static char line[LINE_MAX];
176 pid_t *pids = 0, *tpids;
177 int pid_index = 0;
179 #ifdef __svr4__
180 int fd;
181 struct pstatus pstat;
182 #else
183 FILE *fp;
184 #endif
186 line[0] = '\0';
188 if (!procdir && firstrun)
189 return "!";
191 if (!procdir) {
192 firstrun = 1;
194 if ((procdir = opendir("/proc")) == NULL) {
195 warn("%s", "/proc");
196 return "!";
200 rewinddir(procdir);
202 again:
203 while ((ent = readdir(procdir)) != NULL) {
204 pid_t pid = -1;
205 char filename[FILENAME_MAX];
206 char buf[LINE_MAX];
207 int i;
209 #ifndef __svr4__
210 char *t;
211 #endif
213 if (!isdigit((unsigned char) *ent->d_name))
214 continue;
216 #ifdef __linux__
217 snprintf(filename, sizeof(filename), "/proc/%s/stat", ent->d_name);
218 #else
219 snprintf(filename, sizeof(filename), "/proc/%s/status", ent->d_name);
220 #endif
222 if (stat(filename, &st) == -1)
223 continue;
226 * The current user owns this file (process id).
228 if (st.st_uid == uid) {
229 #ifdef __svr4__
230 if ((fd = open(filename, O_RDONLY)) == -1)
231 continue;
233 if (pread(fd, &pstat, sizeof(struct pstatus), 0) !=
234 sizeof(struct pstatus)) {
235 close(fd);
236 continue;
239 pid = pstat.pr_ppid;
240 close(fd);
241 #else
242 if ((fp = fopen(filename, "r")) == NULL)
243 continue;
245 if ((t = fgets(buf, sizeof(buf), fp)) == NULL) {
246 fclose(fp);
247 continue;
250 #ifdef __linux__
251 if ((i = sscanf(buf, "%*i %*s %*c %*i %*i %i", &pid)) < 1) {
252 #endif
254 #else
255 if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) {
256 #endif
258 fclose(fp);
259 continue;
262 fclose(fp);
263 #endif
266 * Skip duplicate pids.
268 for (i = 0; i < pid_index; i++) {
269 if (pids[i] == pid)
270 goto again;
273 snprintf(buf, sizeof(buf), "%li%c", (unsigned long) pid, multi);
274 safe_strncat(line, buf, sizeof(line));
276 if ((tpids =
277 realloc(pids, (pid_index + 2) * sizeof(pid_t *))) == NULL) {
278 warn("realloc()");
279 continue;
282 pids = tpids;
283 pids[pid_index++] = pid;
287 if (pid_index)
288 free(pids);
290 if (line[0] == '\0')
291 return "!";
293 line[strlen(line) - 1] = '\0';
294 return line;
296 #else
297 /* Unsupported OS. */
298 static char *get_pid(uid_t uid, int multi)
300 return "!";
302 #endif
303 #endif
305 /* Break up the last login string into sections and add the sections to the
306 * output string array if needed. */
307 static void last_strings(char *str)
309 int i = 0;
310 char *buf;
311 const char *line, *host, *when;
313 line = host = when = (str) ? "-" : "!";
315 while ((buf = strsep(&str, ",")) != NULL) {
316 if (!buf[0])
317 continue;
319 switch (i++) {
320 case 0:
321 line = buf;
322 break;
323 case 1:
324 host = buf;
325 break;
326 case 2:
327 when = buf;
328 break;
329 default:
330 break;
334 for (i = 0; i < strlen(last_options); i++) {
335 switch (last_options[i]) {
336 case 'y':
337 add_string(&strings, line);
338 break;
339 case 'h':
340 add_string(&strings, host);
341 break;
342 case 't':
343 add_string(&strings, when);
344 break;
345 case 'a':
346 add_string(&strings, line);
347 add_string(&strings, host);
348 add_string(&strings, when);
349 default:
350 break;
355 /* Get the lastlog structure from the lastlog file. */
356 #if __FreeBSD_version >= 900000
357 static char *lastlogin(const struct passwd *pw, char *tf)
359 struct utmpx *last;
360 static char buf[LINE_MAX];
362 if (setutxdb(UTXDB_LASTLOGIN, NULL) == -1) {
363 warn("lastlog");
364 return NULL;
367 last = getutxuser(pw->pw_name);
369 if (!last)
370 return NULL;
372 snprintf(buf, sizeof(buf), "%s,%s,%s",
373 !last->ut_line[0] ? "!" : last->ut_line,
374 (!last->ut_host[0] || !isalnum(last->ut_host[0])) ?
375 !isdigit(last->ut_line[3]) ? "!" : "-" : last->ut_host,
376 !last->ut_time[0] ? "!" : stamp(last->ut_time, tf));
378 return buf;
380 #else
381 static char *lastlogin(const struct passwd *pw, char *tf)
383 int count;
384 long offset;
385 static char buf[LINE_MAX];
386 struct lastlog last;
388 if (lastlogfd < 0)
389 return NULL;
391 if (!lastlogfd) {
392 if ((lastlogfd = open(_PATH_LASTLOG, O_RDONLY)) == -1) {
393 warn("%s", _PATH_LASTLOG);
394 return NULL;
398 offset = (long) pw->pw_uid * sizeof(struct lastlog);
400 if (lseek(lastlogfd, offset, SEEK_SET) == -1) {
401 warn("%s", _PATH_LASTLOG);
402 return NULL;
405 if ((count = read(lastlogfd, &last, sizeof(struct lastlog))) !=
406 sizeof(struct lastlog)) {
407 if (count == -1)
408 warn("%s", _PATH_LASTLOG);
410 return NULL;
413 #ifdef __NetBSD__
414 #ifdef HAVE_UTMPX_H
415 last.ll_host[UTX_HOSTSIZE-1] = '\0';
416 last.ll_line[UTX_LINESIZE-1] = '\0';
417 #else
418 last.ll_host[UT_HOSTSIZE-1] = '\0';
419 last.ll_line[UT_LINESIZE-1] = '\0';
420 #endif
421 #else
422 last.ll_host[UT_HOSTSIZE-1] = '\0';
423 last.ll_line[UT_LINESIZE-1] = '\0';
424 #endif
426 snprintf(buf, sizeof(buf), "%s,%s,%s",
427 !last.ll_line[0] ? "!" : last.ll_line,
428 (!last.ll_host[0] || !isalnum(last.ll_host[0])) ?
429 !isdigit(last.ll_line[3]) ? "!" : "-" : last.ll_host,
430 !last.ll_time ? "!" : stamp(last.ll_time, tf));
431 return buf;
433 #endif
435 /* This will return an array of utmp structures if a user is logged in, NULL
436 * otherwise. We'll try to keep the utmp file descriptor open if possible to
437 * speed things up a bit. */
438 static UTMP **get_utmp(const char *user)
440 UTMP **logins = NULL;
441 #ifdef HAVE_UTMPX_H
442 UTMP *u;
443 #else
444 UTMP u;
445 int count;
446 static int fd;
448 if (fd < 0)
449 return NULL;
451 if (!fd) {
452 if ((fd = open(_PATH_UTMP, O_RDONLY)) == -1) {
453 warn("%s", _PATH_UTMP);
454 return NULL;
457 #endif
459 login_count = 0;
461 #ifdef HAVE_UTMPX_H
462 setutxent();
464 while ((u = getutxent()) != NULL) {
465 if (!strcmp(u->ut_user, user) && u->ut_type != DEAD_PROCESS) {
466 #else
467 lseek(fd, 0, SEEK_SET);
469 while ((count = read(fd, &u, sizeof(UTMP))) == sizeof(UTMP)) {
470 if (strcmp(u.ut_name, user) == 0) {
471 #endif
472 UTMP **tmp;
474 if ((tmp = realloc(logins,
475 (login_count + 2) * sizeof(UTMP *))) ==
476 NULL) {
477 warn("realloc()");
478 free_logins(logins);
479 return NULL;
482 logins = tmp;
484 if ((logins[login_count] = malloc(sizeof(UTMP))) == NULL) {
485 warn("malloc()");
486 free_logins(logins);
487 return NULL;
490 #ifdef HAVE_UTMPX_H
491 #ifdef __NetBSD__
492 memcpy(logins[login_count]->ut_name, u->ut_name, UTX_NAMESIZE);
493 logins[login_count]->ut_name[UTX_NAMESIZE-1] = 0;
494 memcpy(logins[login_count]->ut_line, u->ut_line, UTX_LINESIZE);
495 logins[login_count]->ut_line[UTX_LINESIZE-1] = 0;
496 memcpy(logins[login_count]->ut_host, u->ut_host, UTX_HOSTSIZE);
497 logins[login_count]->ut_host[UTX_HOSTSIZE-1] = 0;
498 logins[login_count]->ut_pid = u->ut_pid;
499 #else
500 memcpy(logins[login_count]->ut_user, u->ut_user, UT_NAMESIZE);
501 logins[login_count]->ut_user[UT_NAMESIZE-1] = 0;
502 memcpy(logins[login_count]->ut_line, u->ut_line, UT_LINESIZE);
503 logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
504 memcpy(logins[login_count]->ut_host, u->ut_host, UT_HOSTSIZE);
505 logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
506 logins[login_count]->ut_tv.tv_sec = u->ut_tv.tv_sec;
507 logins[login_count]->ut_pid = u->ut_pid;
508 #endif
509 #else
510 memcpy(logins[login_count]->ut_name, u.ut_name, UT_NAMESIZE);
511 logins[login_count]->ut_name[UT_NAMESIZE-1] = 0;
512 memcpy(logins[login_count]->ut_line, u.ut_line, UT_LINESIZE);
513 logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
514 memcpy(logins[login_count]->ut_host, u.ut_host, UT_HOSTSIZE);
515 logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
516 logins[login_count]->ut_time = u.ut_time;
517 #endif
518 logins[++login_count] = NULL;
522 return logins;
525 /* The 'mesg' status of the logged in user. */
526 static char *msgstat(UTMP **u, int multi)
528 static char line[LINE_MAX];
529 int i;
531 line[0] = '\0';
533 for (i = 0; i < login_count; i++) {
534 char filename[FILENAME_MAX];
535 struct stat st;
536 char m[2] = { multi, '\0' };
538 snprintf(filename, sizeof(filename), "%s%s", _PATH_DEV, u[i]->ut_line);
540 if (stat(filename, &st) == -1)
541 safe_strncat(line, "!", sizeof(line));
542 else
543 safe_strncat(line,
544 (st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) ? "1" : "0",
545 sizeof(line));
547 safe_strncat(line, m, sizeof(line));
550 if (line[0] == '\0')
551 return "!";
553 line[strlen(line) - 1] = '\0';
554 return line;
557 /* Returns the users idle time in seconds. */
558 static char *idle(UTMP **u, int multi)
560 static char line[LINE_MAX];
561 time_t t;
562 struct stat st;
563 int i;
565 line[0] = '\0';
567 for (i = 0; i < login_count; i++) {
568 char buf[FILENAME_MAX];
569 char m[2] = { multi, '\0' };
571 snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, u[i]->ut_line);
573 if (stat(buf, &st) == -1) {
574 safe_strncat(line, "!", sizeof(line));
575 safe_strncat(line, m, sizeof(line));
576 continue;
579 #ifdef HAVE_UTMPX_H
580 if (u[i]->ut_tv.tv_sec > st.st_atime) {
581 #else
582 if (u[i]->ut_time > st.st_atime) {
583 #endif
584 safe_strncat(line, "-", sizeof(line));
585 safe_strncat(line, m, sizeof(line));
586 continue;
589 t = st.st_atime;
591 #ifdef HAVE_UTMPX_H
592 if (t < u[i]->ut_tv.tv_sec)
593 t = u[i]->ut_tv.tv_sec;
594 #else
595 if (t < u[i]->ut_time)
596 t = u[i]->ut_time;
597 #endif
599 snprintf(buf, sizeof(buf), "%lu", (now - t <= 0) ? 0 : now - t);
600 safe_strncat(line, buf, sizeof(line));
601 safe_strncat(line, m, sizeof(line));
604 if (line[0] == '\0')
605 return "!";
607 line[strlen(line) - 1] = '\0';
608 return line;
611 /* This is output if the -h command line option is passed to the main program.
613 void ui_module_help()
615 printf(" Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER);
616 printf("\t-y tty\t\t\t\t");
617 printf("-m message status\n");
618 printf("\t-t login time stamp\t\t");
619 printf("-d duration in minutes\n");
620 printf("\t-h hostname\t\t\t");
621 printf("-i seconds idle\n");
622 printf("\t-p login process id\n");
623 printf("\t-l lastlog information"
624 " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n");
625 return;
628 /* This is the equivalent to main() only without argc and argv available. */
629 int ui_module_exec(char ***s, const struct passwd *pw, const int multi,
630 const int verbose, char *tf)
632 char *p = options;
633 UTMP **u = NULL;
634 char buf[255];
636 login_count = 0;
637 u = get_utmp(pw->pw_name);
638 strings = *s;
640 while (*p) {
641 char line[LINE_MAX] = { '\0' };
642 int i;
643 char m[2] = { multi, '\0' };
645 switch (*p) {
646 case 'i':
647 add_string(&strings, (u) ? idle(u, multi) : "!");
648 break;
649 case 'l':
650 last_strings(lastlogin(pw, tf));
651 break;
652 case 'h':
653 for (i = 0; i < login_count; i++) {
654 if (u[i]->ut_host[0]
655 && isalnum((unsigned char) u[i]->ut_host[0])) {
656 safe_strncat(line, u[i]->ut_host, sizeof(line));
658 else {
660 * If a users tty is tty1-n, it must be a console
661 * * login.
663 if (u[i]->ut_line[0]
664 && isdigit((unsigned char) u[i]->ut_line[3]))
665 safe_strncat(line, "-", sizeof(line));
666 else
667 safe_strncat(line, "!", sizeof(line));
670 safe_strncat(line, m, sizeof(line));
673 if (line[0] == '\0')
674 strncpy(line, "!", sizeof(line));
675 else
676 line[strlen(line) - 1] = '\0';
678 add_string(&strings, line);
679 break;
680 case 'y':
681 for (i = 0; i < login_count; i++) {
682 if (u[i]->ut_line[0])
683 safe_strncat(line, u[i]->ut_line, sizeof(line));
684 else
685 safe_strncat(line, "!", sizeof(line));
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 'm':
698 add_string(&strings, msgstat(u, multi));
699 break;
700 case 't':
701 for (i = 0; i < login_count; i++) {
702 #ifdef HAVE_UTMPX_H
703 safe_strncat(line, stamp(u[i]->ut_tv.tv_sec, tf), sizeof(line));
704 #else
705 safe_strncat(line, stamp(u[i]->ut_time, tf), sizeof(line));
706 #endif
707 safe_strncat(line, m, sizeof(line));
710 if (line[0] == '\0')
711 strncpy(line, "!", sizeof(line));
712 else
713 line[strlen(line) - 1] = '\0';
715 add_string(&strings, line);
716 break;
717 case 'd':
718 for (i = 0; i < login_count; i++) {
719 #ifdef HAVE_UTMPX_H
720 if ((now - u[i]->ut_tv.tv_sec) > 60) {
721 snprintf(buf, sizeof(buf), "%lu",
722 ((now - u[i]->ut_tv.tv_sec) / 60));
723 #else
724 if ((now - u[i]->ut_time) > 60) {
725 snprintf(buf, sizeof(buf), "%lu",
726 ((now - u[i]->ut_time) / 60));
727 #endif
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 break;
743 case 'p':
744 #ifdef HAVE_UTMPX_H
745 for (i = 0; i < login_count; i++) {
746 if (u[i]->ut_pid) {
747 snprintf(buf, sizeof(buf), "%li", (long) u[i]->ut_pid);
748 safe_strncat(line, buf, sizeof(line));
750 else
751 safe_strncat(line, "!", sizeof(line));
753 safe_strncat(line, m, sizeof(line));
756 if (line[0] == '\0')
757 strncpy(line, "!", sizeof(line));
758 else
759 line[strlen(line) - 1] = '\0';
761 add_string(&strings, line);
762 #else
763 add_string(&strings, (u) ? get_pid(pw->pw_uid, multi) : "!");
764 #endif
765 break;
766 default:
767 break;
770 p++;
773 free_logins(u);
774 *s = strings;
775 return EXIT_SUCCESS;
778 /* See if the last login options (-l) are valid. */
779 static int parse_last_options(const char *args)
781 int i = 0;
783 for (i = 0; i < strlen(args); i++) {
784 switch (args[i]) {
785 case 'y':
786 case 'h':
787 case 't':
788 case 'a':
789 break;
790 default:
791 return 1;
795 return 0;
798 char *ui_module_options_init(char **defaults)
800 *defaults = "L";
801 return LOGIN_OPTION_STRING;
804 /* Check module option validity. */
805 int ui_module_options(int argc, char **argv)
807 int opt;
808 char *p = options;
810 while ((opt = getopt(argc, argv, LOGIN_OPTION_STRING)) != -1) {
811 switch (opt) {
812 case 'l':
813 if (parse_last_options(optarg))
814 return 1;
816 last_options = optarg;
817 break;
818 case 'L':
819 strncpy(options, LOGIN_OPTION_ORDER, sizeof(options));
820 last_options = "a";
821 return 0;
822 case 'p':
823 case 'd':
824 case 'i':
825 case 'm':
826 case 'y':
827 case 'h':
828 case 't':
829 break;
830 case '?':
831 warnx("login: invalid option -- %c", optopt);
832 default:
833 return 1;
836 *p++ = opt;
837 *p = '\0';
840 return 0;