Hello all, I’ve been experimenting with whowatch in my personal fork and have successfully replaced wtmp/utmp with the utmpx API. It’s been working well so far, except for the sysinfo plugin, which is currently unable to read system information on FreeBSD. Otherwise, the core functionality seems fine.
Repository & branch: https://github.com/Zedai00/whowatch/tree/freebsd-utmpx-experiment I’ve also generated a format-patch diff for all changes, which is attached. If anyone can test this on their FreeBSD machine, it would be greatly appreciated. Feedback on the sysinfo issue would be especially useful. Thanks in advance!
From 4f5be1f2ee9cb85068843bf6fd46fcbd6c53ca71 Mon Sep 17 00:00:00 2001 From: Jishan Alam <[email protected]> Date: Thu, 8 Jan 2026 02:48:51 +0530 Subject: [PATCH] Update whowatch to use utmpx on FreeBSD - Replace wtmp and utmp with utmpx API - Fix header related issues - Prevent same user being added repeatedly - Add configure checks for utmpx - Fix undefined proc_login_pid --- configure.ac | 142 +++++------- src/list.h | 95 ++++---- src/proc_plugin.c | 2 +- src/procinfo.h | 1 + src/ulist.c | 576 +++++++++++++++++++++++----------------------- src/ulist.h | 73 +++--- src/var.h | 22 +- src/whowatch.h | 208 +++++++++-------- 8 files changed, 536 insertions(+), 583 deletions(-) diff --git a/configure.ac b/configure.ac index d012f26..868c46a 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_INIT([whowatch],[1.8.6],[https://github.com/mtsuszycki/whowatch/issues],[whow AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_SRCDIR([src/whowatch.c]) -AC_CONFIG_HEADER(src/config.h) +AC_CONFIG_HEADER([src/config.h]) dnl Checks for programs. AC_PROG_CC @@ -29,119 +29,85 @@ AC_PROG_MAKE_SET LDFLAGS="$LDFLAGS -rdynamic" -dnl Checks for libraries. -dnl Replace `main' with a function in -lkvm: -AC_CHECK_LIB(kvm, kvm_openfiles) -dnl Replace `main' with a function in -lncurses: +dnl Checks for libraries +AC_CHECK_LIB([kvm], [kvm_openfiles]) CURS=0 -AC_CHECK_LIB(ncurses, scrollok, [CURS=1 ; LIBS="$LIBS -lncurses"]) -if test "$CURS" != "1"; then - AC_CHECK_LIB(curses, scrollok, [CURS=1 ; LIBS="$LIBS -lcurses"]) +AC_CHECK_LIB([ncurses], [scrollok], [CURS=1; LIBS="$LIBS -lncurses"]) +if test "$CURS" != "1"; then + AC_CHECK_LIB([curses], [scrollok], [CURS=1; LIBS="$LIBS -lcurses"]) fi if test "$CURS" != "1"; then - AC_MSG_ERROR([Could not find proper curses library]) + AC_MSG_ERROR([Could not find proper curses library]) fi -dnl Checks for header files. +dnl Checks for header files AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/time.h unistd.h) -AC_CHECK_HEADERS(curses.h) -AC_CHECK_HEADERS([kvm.h stdint.h]) -AC_CHECK_HEADERS([asm/param.h]) -AC_CHECK_HEADERS([arpa/inet.h]) -AC_CHECK_HEADERS([netinet/in.h]) -AC_CHECK_HEADERS([sys/param.h]) -AC_CHECK_HEADERS([termios.h]) -AC_CHECK_HEADERS([utmp.h]) +AC_CHECK_HEADERS([fcntl.h sys/ioctl.h sys/time.h unistd.h curses.h kvm.h stdint.h]) +AC_CHECK_HEADERS([asm/param.h arpa/inet.h netinet/in.h sys/param.h termios.h utmpx.h]) +AC_HEADER_TIME + +dnl Check for utmpx functions +AC_CHECK_FUNCS([setutxent getutxent endutxent]) -dnl Checks for typedefs, structures, and compiler characteristics. +dnl Checks for typedefs, structures, and compiler characteristics AC_C_CONST AC_C_INLINE AC_TYPE_PID_T AC_TYPE_SIZE_T AC_CHECK_MEMBERS([struct stat.st_rdev]) -AC_HEADER_TIME - -AC_MSG_CHECKING([whether utmp has USER_PROCESS type]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include <sys/types.h> -#include <utmp.h> -]], [[int i=USER_PROCESS;]])],[AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_USER_PROCESS],[1],[define if utmp has USER_PROCESS type])],[AC_MSG_RESULT(no)]) - -AC_MSG_CHECKING([whether utmp has DEAD_PROCESS type]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include <sys/types.h> -#include <utmp.h> -]], [[int i=DEAD_PROCESS;]])],[AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_DEAD_PROCESS],[1],[define if utmp has DEAD_PROCESS type])],[AC_MSG_RESULT(no)]) - -AC_MSG_CHECKING([whether utmp struct has ut_pid member]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include <sys/types.h> -#include <utmp.h> -]], [[struct utmp u;int i=u.ut_pid;]])],[AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_UTPID],[1],[define if utmp struct has ut_pid member])],[AC_MSG_RESULT(no)]) - -AC_MSG_CHECKING([whether utmp struct has ut_name member]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include <sys/types.h> -#include <utmp.h> -]], [[struct utmp u;char *c=u.ut_name;]])],[AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_UT_NAME],[1],[define if utmp struct has ut_name member])],[AC_MSG_RESULT(no)]) -dnl Checks for library functions. +dnl Check for library functions AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC -AC_CHECK_FUNCS(select) -AC_CHECK_FUNCS(getloadavg) -AC_CHECK_FUNCS([bzero]) -AC_CHECK_FUNCS([gettimeofday]) -AC_CHECK_FUNCS([inet_ntoa]) -AC_CHECK_FUNCS([isascii]) -AC_CHECK_FUNCS([memset]) -AC_CHECK_FUNCS([regcomp]) -AC_CHECK_FUNCS([strchr]) -AC_CHECK_FUNCS([strncasecmp]) -AC_CHECK_FUNCS([strrchr]) -AC_CHECK_FUNCS([sysconf]) +AC_CHECK_FUNCS([select getloadavg bzero gettimeofday inet_ntoa isascii memset regcomp strchr strncasecmp strrchr sysconf]) +dnl Checks whether select() modifies the timeval AC_MSG_CHECKING([whether select() modifies the time value]) -AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <sys/types.h> +AC_RUN_IFELSE( + [AC_LANG_SOURCE([ +#include <sys/types.h> #include <sys/time.h> #include <unistd.h> -int main() -{ -int retval; -fd_set rfds; -struct timeval tv = {1, 0}; -FD_ZERO(&rfds); FD_SET(0,&rfds); -select(1,&rfds,0,0,&tv); -if(tv.tv_sec == 0) return 0; -else return 1; +int main() { + int retval; + fd_set rfds; + struct timeval tv = {1, 0}; + FD_ZERO(&rfds); FD_SET(0, &rfds); + select(1, &rfds, 0, 0, &tv); + return (tv.tv_sec == 0) ? 0 : 1; } -]])],[AC_MSG_RESULT(yes); AC_DEFINE([RETURN_TV_IN_SELECT],[1],[define if select() modifies the time value])],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)]) - + ])], + [AC_MSG_RESULT(yes) + AC_DEFINE([RETURN_TV_IN_SELECT], [1], [define if select() modifies the time value]) + ], + [AC_MSG_RESULT(no)] +) + +dnl Check whether sysctl() can be used AC_MSG_CHECKING([whether sysctl() can be used]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include <sys/types.h> #include <sys/param.h> -#include <sys/sysctl.h>]], [[ -int mib[]={CTL_KERN,KERN_PROC,KERN_PROC_PID,1}; -size_t len; -sysctl(mib, 4, 0, &len, 0, 0); -]])],[AC_MSG_RESULT(yes); HAVE_PROCESS_SYSCTL=yes; AC_DEFINE([HAVE_PROCESS_SYSCTL],[1],[define if sysctl() can be used])],[AC_MSG_RESULT(no)]) - -AC_CHECK_MEMBERS( -[struct kinfo_proc.kp_proc],,, +#include <sys/sysctl.h> +])], + [AC_MSG_RESULT(yes) + HAVE_PROCESS_SYSCTL=yes + AC_DEFINE([HAVE_PROCESS_SYSCTL], [1], [define if sysctl() can be used]) + ], + [AC_MSG_RESULT(no)] +) + +dnl Check members of struct kinfo_proc +AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc], , , [#include <sys/types.h> - #include <sys/param.h> - #include <sys/sysctl.h> - #include <sys/proc.h> - ]) +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/proc.h> +]) AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT - diff --git a/src/list.h b/src/list.h index f580e31..bd5d844 100644 --- a/src/list.h +++ b/src/list.h @@ -31,17 +31,20 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. * generate better code by using them directly rather than * using the generic single-entry routines. */ +#ifndef _WHOWATCH_LIST_H_ +#define _WHOWATCH_LIST_H_ struct list_head { - struct list_head *next, *prev; + struct list_head *next, *prev; }; -#define DECL_LIST_HEAD(name) \ - struct list_head name = { &name, &name } +#define DECL_LIST_HEAD(name) struct list_head name = {&name, &name} -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) +#define INIT_LIST_HEAD(ptr) \ + do { \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + } while (0) /* * Insert a new entry between two known consecutive entries. @@ -49,22 +52,19 @@ struct list_head { * This is only for internal list manipulation where we know * the prev/next entries already! */ -static __inline__ void __list_add(struct list_head * new, - struct list_head * prev, - struct list_head * next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; +static __inline__ void __list_add(struct list_head *new, struct list_head *prev, + struct list_head *next) { + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; } /* * Insert a new entry after the specified head.. */ -static __inline__ void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); +static __inline__ void list_add(struct list_head *new, struct list_head *head) { + __list_add(new, head, head->next); } /* @@ -74,54 +74,51 @@ static __inline__ void list_add(struct list_head *new, struct list_head *head) * This is only for internal list manipulation where we know * the prev/next entries already! */ -static __inline__ void __list_del(struct list_head * prev, - struct list_head * next) -{ - next->prev = prev; - prev->next = next; +static __inline__ void __list_del(struct list_head *prev, + struct list_head *next) { + next->prev = prev; + prev->next = next; } -static __inline__ void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); +static __inline__ void list_del(struct list_head *entry) { + __list_del(entry->prev, entry->next); } -static __inline__ void LIST_DEL(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); +static __inline__ void LIST_DEL(struct list_head *entry) { + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); } -static __inline__ int list_empty(struct list_head *head) -{ - return head->next == head; +static __inline__ int list_empty(struct list_head *head) { + return head->next == head; } /* * Splice in "list" into "head" */ -static __inline__ void list_splice(struct list_head *list, struct list_head *head) -{ - struct list_head *first = list->next; +static __inline__ void list_splice(struct list_head *list, + struct list_head *head) { + struct list_head *first = list->next; - if (first != list) { - struct list_head *last = list->prev; - struct list_head *at = head->next; + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; - first->prev = head; - head->next = first; + first->prev = head; + head->next = first; - last->next = at; - at->prev = last; - } + last->next = at; + at->prev = last; + } } -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) -#define list_for_each_r(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) +#define list_for_each_r(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) +#endif diff --git a/src/proc_plugin.c b/src/proc_plugin.c index b09c0e4..560c765 100644 --- a/src/proc_plugin.c +++ b/src/proc_plugin.c @@ -55,7 +55,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. #if defined(HAVE_LIBKVM) && defined(HAVE_STDINT_H) && defined(HAVE_KVM_H) #include <stdint.h> #include <kvm.h> -kvm_t *kd; +// kvm_t *kd; extern int can_use_kvm; #endif diff --git a/src/procinfo.h b/src/procinfo.h index 3e03a08..aaa1931 100644 --- a/src/procinfo.h +++ b/src/procinfo.h @@ -32,4 +32,5 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. #ifdef HAVE_LIBKVM #include <kvm.h> +extern kvm_t *kd; #endif diff --git a/src/ulist.c b/src/ulist.c index 8b1d27b..035649d 100644 --- a/src/ulist.c +++ b/src/ulist.c @@ -1,382 +1,378 @@ -/* - -SPDX-License-Identifier: GPL-2.0+ - -Copyright 2001, 2006 Michal Suszycki <[email protected]> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ - -#include "whowatch.h" -#include "config.h" #include "ulist.h" +#include "config.h" +#include "whowatch.h" /* indexes in prot_tab[] */ -#define SSH 0 -#define TELNET 1 -#define LOCAL 2 +#define SSH 0 +#define TELNET 1 +#define LOCAL 2 static struct prot_t prot_tab[] = { #ifdef HAVE_PROCESS_SYSCTL - { "sshd", 22, 0 }, { "telnetd", 23, 0 }, { "init", -1, 0 } + {"sshd", 22, 0}, {"telnetd", 23, 0}, {"init", -1, 0} #else - { "(sshd", 22, 0 }, { "(in.telnetd)", 23, 0 }, { "(init)", -1, 0 } + {"(sshd", 22, 0}, {"(in.telnetd)", 23, 0}, {"(init)", -1, 0} #endif }; -//static char *hlp = "\001[F1]Help [F9]Menu [ENT]proc all[t]ree [i]dle/cmd [c]md [d]etails [s]ysinfo", +// static char *hlp = "\001[F1]Help [F9]Menu [ENT]proc all[t]ree [i]dle/cmd +// [c]md [d]etails [s]ysinfo", static char *hlp = "[ENT]proc all[t]ree [d]etails [s]ysinfo"; static u32 nusers; static struct wdgt *self; -static void u_count(char *name, int p) -{ - int i; - struct prot_t *t; - nusers += p; - for(i = 0; i < sizeof prot_tab/sizeof(struct prot_t); i++){ - t = &prot_tab[i]; - if(strncmp(t->s, name, strlen(t->s))) continue; - t->nr += p; - } +static void u_count(char *name, int p) { + int i; + struct prot_t *t; + nusers += p; + for (i = 0; i < sizeof prot_tab / sizeof(struct prot_t); i++) { + t = &prot_tab[i]; + if (strncmp(t->s, name, strlen(t->s))) + continue; + t->nr += p; + } } /* * After deleting line, update line numbers in each user structure */ -void update_line(int line) -{ - struct user_t *u; - struct list_head *tmp; - list_for_each(tmp, &users_l) { - u = list_entry(tmp, struct user_t, head); - if(u->line > line) u->line--; - } +void update_line(int line) { + struct user_t *u; + struct list_head *tmp; + list_for_each(tmp, &users_l) { + u = list_entry(tmp, struct user_t, head); + if (u->line > line) + u->line--; + } } /* * Create new user structure and fill it */ -struct user_t *alloc_user(struct utmp *entry) -{ - struct user_t *u; - int ppid; - - u = calloc(1, sizeof *u); - if(!u) errx(1, "Cannot allocate memory."); - strncpy(u->name, entry->ut_user, UT_NAMESIZE); - strncpy(u->tty, entry->ut_line, UT_LINESIZE); - strncpy(u->host, entry->ut_host, UT_HOSTSIZE); -#ifdef HAVE_UTPID - u->pid = entry->ut_pid; -#else - u->pid = get_login_pid(u->tty); -#endif - if((ppid = get_ppid(u->pid)) == -1) - strncpy(u->parent, "can't access", sizeof u->parent); - else strncpy(u->parent, get_name(ppid), sizeof u->parent - 1); - u->line = nusers; - return u; +struct user_t *alloc_user(struct utmpx *entry) { + struct user_t *u; + int ppid; + + u = calloc(1, sizeof *u); + if (!u) + errx(1, "Cannot allocate memory."); + + strncpy(u->name, entry->ut_user, UT_NAMESIZE); + strncpy(u->tty, entry->ut_line, UT_LINESIZE); + strncpy(u->host, entry->ut_host, UT_HOSTSIZE); + + u->pid = entry->ut_pid; + + if ((ppid = get_ppid(u->pid)) == -1) + strncpy(u->parent, "can't access", sizeof u->parent); + else + strncpy(u->parent, get_name(ppid), sizeof u->parent - 1); + u->line = nusers; + + return u; } -static struct user_t* new_user(struct utmp *ut) -{ - struct user_t *u; - u = alloc_user(ut); - list_add(&u->head, &users_l); - u_count(u->parent, LOGIN); - return u; +static struct user_t *new_user(struct utmpx *ut) { + struct user_t *u; + u = alloc_user(ut); + list_add(&u->head, &users_l); + u_count(u->parent, LOGIN); + return u; } -static void uprint(struct user_t *u, struct wdgt *w) -{ - int n; - scr_attr_set(w, A_BOLD); - n = snprintf(w->mwin->gbuf, w->mwin->gbsize, "%-14.14s %-9.9s %-6.6s %-19.19s %s", - u->parent, u->name, u->tty, u->host, - toggle?count_idle(u->tty):get_w(u->pid)); - scr_maddstr(w, w->mwin->gbuf, u->line, 0, n); +static void uprint(struct user_t *u, struct wdgt *w) { + int n; + scr_attr_set(w, A_BOLD); + n = snprintf(w->mwin->gbuf, w->mwin->gbsize, + "%-14.14s %-9.9s %-6.6s %-19.19s %s", u->parent, u->name, u->tty, + u->host, toggle ? count_idle(u->tty) : get_w(u->pid)); + scr_maddstr(w, w->mwin->gbuf, u->line, 0, n); } -void uredraw(struct wdgt *w) -{ - struct list_head *tmp; - struct user_t *u; - scr_werase(w); - scr_output_start(w); - list_for_each_r(tmp, &users_l) { - u = list_entry(tmp, struct user_t, head); - uprint(u, w); - } - scr_output_end(w); +void uredraw(struct wdgt *w) { + struct list_head *tmp; + struct user_t *u; + scr_werase(w); + scr_output_start(w); + list_for_each_r(tmp, &users_l) { + u = list_entry(tmp, struct user_t, head); + uprint(u, w); + } + scr_output_end(w); } /* * Gather information about users currently on the machine * Called only at start or restart */ -void read_utmp(void) -{ - int fd, i; - static struct utmp entry; - struct user_t *u; - - if ((fd = open(UTMP_FILE ,O_RDONLY)) == -1) err_exit(1, "Cannot open utmp"); - while((i = read(fd, &entry,sizeof entry)) > 0) { - if(i != sizeof entry) errx(1, "Error reading " UTMP_FILE ); +void read_utmpx(void) { + static struct utmpx *entry; + struct user_t *u; + + setutxent(); + while ((entry = getutxent()) != NULL) { + #ifdef HAVE_USER_PROCESS - if(entry.ut_type != USER_PROCESS) continue; + if (entry->ut_type != USER_PROCESS) + continue; #else - if(!entry.ut_name[0]) continue; + if (!entry->ut_user[0]) + continue; #endif - u = new_user(&entry); - } - close(fd); - return; + u = new_user(entry); + } + endutxent(); + return; } /* * get user entry from specific line (cursor position) */ -struct user_t *cursor_user(int line) -{ - struct user_t *u; - struct list_head *h; - DBG("looking for user on line %d", line); -// int line = 0; //current->cursor + current->offset; - list_for_each(h, &users_l) { - u = list_entry(h, struct user_t, head); - if(u->line == line) return u; - } - return 0; +struct user_t *cursor_user(int line) { + struct user_t *u; + struct list_head *h; + DBG("looking for user on line %d", line); + // int line = 0; //current->cursor + current->offset; + list_for_each(h, &users_l) { + u = list_entry(h, struct user_t, head); + if (u->line == line) + return u; + } + return 0; } -static void udel(struct user_t *u, struct wdgt *w) -{ - scr_delline(w, u->line); - scr_ldeleted(w, u->line); - update_line(u->line); - u_count(u->parent, LOGOUT); - list_del(&u->head); - free(u); +static void udel(struct user_t *u, struct wdgt *w) { + scr_delline(w, u->line); + scr_ldeleted(w, u->line); + update_line(u->line); + u_count(u->parent, LOGOUT); + list_del(&u->head); + free(u); } -char *proc_ucount(void) -{ - static char buf[64]; +char *proc_ucount(void) { + static char buf[64]; - int other = nusers - prot_tab[LOCAL].nr - prot_tab[TELNET].nr - prot_tab[SSH].nr; - snprintf(buf, sizeof buf - 1,"\x1%d users: %d local, %d telnet, %d ssh, %d other\x3", - nusers, prot_tab[LOCAL].nr, prot_tab[TELNET].nr, prot_tab[SSH].nr, other); - return buf; + int other = + nusers - prot_tab[LOCAL].nr - prot_tab[TELNET].nr - prot_tab[SSH].nr; + snprintf(buf, sizeof buf - 1, + "\x1%d users: %d local, %d telnet, %d ssh, %d other\x3", nusers, + prot_tab[LOCAL].nr, prot_tab[TELNET].nr, prot_tab[SSH].nr, other); + return buf; } -static void open_wtmp(int *wtmp_fd) -{ - if((*wtmp_fd = open(WTMP_FILE ,O_RDONLY)) == -1) err_exit(1, "Cannot open wtmp"); - if(lseek(*wtmp_fd, 0, SEEK_END) == -1) err_exit(1, "Cannot seek end wtmp"); +/* + * find existing user by tty + */ + +static struct user_t *find_user_by_tty(const char *tty) { + struct list_head *h; + struct user_t *u; + + list_for_each(h, &users_l) { + u = list_entry(h, struct user_t, head); + if (!strncmp(u->tty, tty, UT_LINESIZE)) + return u; + } + return NULL; } /* - * Check wtmp for logouts or new logins + * Check utmpx for logouts or new logins */ -static void check_wtmp(struct wdgt *w) -{ - static int wtmp_fd; - struct user_t *u; - struct list_head *h; - struct utmp entry; - int i, changed = 0; - if(!wtmp_fd) open_wtmp(&wtmp_fd); - - while((i = read(wtmp_fd, &entry, sizeof entry)) > 0){ - if (i < sizeof entry) prg_exit("Error reading wtmp"); - /* user just logged in */ +static void check_utmpx(struct wdgt *w) { + struct user_t *u; + struct list_head *h; + struct utmpx *entry; + int changed = 0; + + setutxent(); + while ((entry = getutxent()) != NULL) { + + /* user just logged in */ #ifdef HAVE_USER_PROCESS - if(entry.ut_type == USER_PROCESS) { + if (entry->ut_type == USER_PROCESS) { #else - if(entry.ut_user[0]) { + if (entry->ut_user[0]) { #endif - u = new_user(&entry); - changed = 1; - continue; - } + if (!find_user_by_tty(entry->ut_line)) { + new_user(entry); + changed = 1; + } + continue; + } #ifdef HAVE_DEAD_PROCESS - if(entry.ut_type != DEAD_PROCESS) continue; -#else -// if(entry.ut_line[0]) continue; + if (entry->ut_type != DEAD_PROCESS) + continue; #endif - /* user just logged out */ - list_for_each(h, &users_l) { - u = list_entry(h, struct user_t, head); - if(strncmp(u->tty, entry.ut_line, UT_LINESIZE)) - continue; - udel(u, w); - changed = 1; - break; - } - } - if(!changed) return; + /* user just logged out */ + list_for_each(h, &users_l) { + u = list_entry(h, struct user_t, head); + if (strncmp(u->tty, entry->ut_line, UT_LINESIZE)) + continue; + udel(u, w); + changed = 1; + break; + } + } + endutxent(); + if (!changed) + return; } -static void ulist_hlp(struct wdgt *w) -{ - DBG("sending %s", hlp); - wmsg_send(w, MCUR_HLP, hlp); +static void ulist_hlp(struct wdgt *w) { + DBG("sending %s", hlp); + wmsg_send(w, MCUR_HLP, hlp); } -void users_init(void) -{ - read_utmp(); -} +void users_init(void) { read_utmpx(); } -static void ulist_periodic(struct wdgt *w) -{ - static int i; +static void ulist_periodic(struct wdgt *w) { + static int i; - if(!i) { - wmsg_send(w, MCUR_HLP, hlp); - i = 1; - } - DBG("ulist periodic"); - check_wtmp(w); + if (!i) { + wmsg_send(w, MCUR_HLP, hlp); + i = 1; + } + DBG("ulist periodic"); + check_utmpx(w); } -static void *cval(void) -{ - static struct user_t *u; +static void *cval(void) { + static struct user_t *u; - u = cursor_user(self->crsr); - if(u) return &u->pid; - return 0; + u = cursor_user(self->crsr); + if (u) + return &u->pid; + return 0; } /* * Needed for search function. If parent, name, tty, host or command line * matches then returns line number of this user. */ -static int user_search(struct wdgt *w, int t) -{ - struct user_t *u; - struct list_head *h; - int l = w->crsr; - - list_for_each(h, &users_l) { - u = list_entry(h, struct user_t, head); - if(!t && u->line <= l) continue; - if(t == 1 && u->line < l) continue; - if(reg_match(u->parent)) goto found; - if(reg_match(u->name)) goto found; - if(reg_match(u->tty)) goto found; - if(reg_match(u->host)) goto found; - if(reg_match(toggle?count_idle(u->tty):get_w(u->pid))) - goto found; - } - return 2; +static int user_search(struct wdgt *w, int t) { + struct user_t *u; + struct list_head *h; + int l = w->crsr; + + list_for_each(h, &users_l) { + u = list_entry(h, struct user_t, head); + if (!t && u->line <= l) + continue; + if (t == 1 && u->line < l) + continue; + if (reg_match(u->parent)) + goto found; + if (reg_match(u->name)) + goto found; + if (reg_match(u->tty)) + goto found; + if (reg_match(u->host)) + goto found; + if (reg_match(toggle ? count_idle(u->tty) : get_w(u->pid))) + goto found; + } + return 2; found: - scr_crsr_jmp(w, u->line); - return 1; + scr_crsr_jmp(w, u->line); + return 1; } -static void *umsgh(struct wdgt *w, int type, struct wdgt *s, void *d) -{ - struct user_t *u; - /* accept it even not visible, ptree can ask about user pid*/ - if(type == MWANT_UPID) { - u = cursor_user(w->crsr); - if(u) return &u->pid; - else return 0; - } - if(WIN_HIDDEN(w)) return 0; -DBG("ulist responded to type %d", type); - switch(type) { - case MALL_CRSR_REG: w->flags |= WDGT_CRSR_SND; break; - case MALL_CRSR_UNREG: w->flags &= ~ WDGT_CRSR_SND; break; - case MWANT_CRSR_VAL: u = cursor_user(w->crsr); - if(u) return u->name; - else return "No user found"; - case MSND_SEARCH: - return (void *)(intptr_t)user_search(w, (u32)d); - break; - } - - return 0; +static void *umsgh(struct wdgt *w, int type, struct wdgt *s, void *d) { + struct user_t *u; + /* accept it even not visible, ptree can ask about user pid*/ + if (type == MWANT_UPID) { + u = cursor_user(w->crsr); + if (u) + return &u->pid; + else + return 0; + } + if (WIN_HIDDEN(w)) + return 0; + DBG("ulist responded to type %d", type); + switch (type) { + case MALL_CRSR_REG: + w->flags |= WDGT_CRSR_SND; + break; + case MALL_CRSR_UNREG: + w->flags &= ~WDGT_CRSR_SND; + break; + case MWANT_CRSR_VAL: + u = cursor_user(w->crsr); + if (u) + return u->name; + else + return "No user found"; + case MSND_SEARCH: + return (void *)(intptr_t)user_search(w, (u32)d); + break; + } + + return 0; } /* * Give up main window if currently printing output there * or start printing if not there */ -static void uswitch(struct wdgt *w, int k, int p) -{ - if(!WIN_HIDDEN(w)) { - DBG("Deleting ulist from lists"); - w->redraw = 0; - w->wrefresh = 0; - } - else { - if(k == 't') return; - ulist_hlp(w); - w->redraw = uredraw; - w->wrefresh = scr_wrefresh; - WNEED_REDRAW(w); - wmsg_send(w, MSND_ESOURCE, euser); - } +static void uswitch(struct wdgt *w, int k, int p) { + if (!WIN_HIDDEN(w)) { + DBG("Deleting ulist from lists"); + w->redraw = 0; + w->wrefresh = 0; + } else { + if (k == 't') + return; + ulist_hlp(w); + w->redraw = uredraw; + w->wrefresh = scr_wrefresh; + WNEED_REDRAW(w); + wmsg_send(w, MSND_ESOURCE, euser); + } } -static void crsr_send(struct wdgt *w) -{ - struct user_t *u; +static void crsr_send(struct wdgt *w) { + struct user_t *u; - if(!(w->flags & WDGT_CRSR_SND)) return; - u = cursor_user(w->crsr); - if(!u) return; - DBG("sending current name %s", u->name); - wmsg_send(w, MCUR_CRSR, u->name); + if (!(w->flags & WDGT_CRSR_SND)) + return; + u = cursor_user(w->crsr); + if (!u) + return; + DBG("sending current name %s", u->name); + wmsg_send(w, MCUR_CRSR, u->name); } - -static int ukeyh(struct wdgt *w, int key) -{ - int ret = KEY_SKIPPED; - static int pkey; - - /* hide/unhide widget */ - if(key == 't' || key == KBD_ENTER) { - uswitch(w, key, pkey); - pkey = key; - return KEY_HANDLED; - } - if(WIN_HIDDEN(w)) return ret; - switch(key) { - default: - ret = scr_keyh(w, key); - if(ret == KEY_HANDLED) crsr_send(w); - } - return ret; +static int ukeyh(struct wdgt *w, int key) { + int ret = KEY_SKIPPED; + static int pkey; + + /* hide/unhide widget */ + if (key == 't' || key == KBD_ENTER) { + uswitch(w, key, pkey); + pkey = key; + return KEY_HANDLED; + } + if (WIN_HIDDEN(w)) + return ret; + switch (key) { + default: + ret = scr_keyh(w, key); + if (ret == KEY_HANDLED) + crsr_send(w); + } + return ret; } -void ulist_reg(struct wdgt *w) -{ - w->periodic = ulist_periodic; - w->redraw = uredraw; - w->wrefresh = scr_wrefresh; - w->keyh = ukeyh; - w->msgh = umsgh; - w->mwin->cval = cval; - mwin_msg_on(w); - self = w; +void ulist_reg(struct wdgt *w) { + w->periodic = ulist_periodic; + w->redraw = uredraw; + w->wrefresh = scr_wrefresh; + w->keyh = ukeyh; + w->msgh = umsgh; + w->mwin->cval = cval; + mwin_msg_on(w); + self = w; } - diff --git a/src/ulist.h b/src/ulist.h index 995c60f..18282af 100644 --- a/src/ulist.h +++ b/src/ulist.h @@ -1,64 +1,45 @@ -/* +#ifndef _ULIST_H_ +#define _ULIST_H_ -SPDX-License-Identifier: GPL-2.0+ +#include <stdint.h> /* uint32_t etc */ +#include <utmpx.h> /* struct utmpx, USER_PROCESS */ -Copyright 2006 Michal Suszycki <[email protected]> +#include "list.h" /* struct list_head, DECL_LIST_HEAD */ +#include "var.h" /* u32 typedefs */ -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ - -#ifndef UTMP_FILE -#define UTMP_FILE "/var/run/utmp" -#endif - -#ifndef WTMP_FILE -#define WTMP_FILE "/var/log/wtmp" -#endif - -#define LOGIN 1 -#define LOGOUT -1 +#define LOGIN 1 +#define LOGOUT -1 #ifdef HAVE_UT_NAME #define ut_user ut_name #endif -struct user_t -{ - struct list_head head; - char name[UT_NAMESIZE + 1]; - char tty[UT_LINESIZE + 1]; - int pid; - char parent[16]; - char host[UT_HOSTSIZE + 1]; - int line; +#define UT_NAMESIZE sizeof(((struct utmpx *)0)->ut_user) +#define UT_LINESIZE sizeof(((struct utmpx *)0)->ut_line) +#define UT_HOSTSIZE sizeof(((struct utmpx *)0)->ut_host) + +struct user_t { + struct list_head head; + char name[UT_NAMESIZE + 1]; + char tty[UT_LINESIZE + 1]; + int pid; + char parent[16]; + char host[UT_HOSTSIZE + 1]; + int line; }; static DECL_LIST_HEAD(users_l); -static int toggle; /* if 0 show cmd line else show idle time */ -//static char *uhelp = "\001[F1]Help [F9]Menu [ENT]proc all[t]ree [i]dle/cmd " +static int toggle; /* if 0 show cmd line else show idle time */ +// static char *uhelp = "\001[F1]Help [F9]Menu [ENT]proc all[t]ree [i]dle/cmd " // " [c]md [d]etails [s]ysinfo"; #ifdef HAVE_PROCESS_SYSCTL int get_login_pid(char *); #endif -struct prot_t -{ - char *s; - short port; - u32 nr; +struct prot_t { + char *s; + short port; + u32 nr; }; - - +#endif diff --git a/src/var.h b/src/var.h index 20287c6..b2a9e03 100644 --- a/src/var.h +++ b/src/var.h @@ -19,19 +19,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#define DU(B) typedef u_int ## B ## _t u ## B -DU(8); DU(16); DU(32); DU(64); -#undef DU +#include <stdint.h> -#define MIN(x,y) (x)<(y)?(x):(y) +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; -//#if 1 +#define MIN(x, y) (x) < (y) ? (x) : (y) + +// #if 1 #ifdef DEBUG -#define DBG(f,a...) dolog("%s %d: " f, __FUNCTION__, __LINE__ , ##a) +#define DBG(f, a...) dolog("%s %d: " f, __FUNCTION__, __LINE__, ##a) #else -#define DBG(f,a...) /* */ +#define DBG(f, a...) /* */ #endif -#define elemof(x) (sizeof (x) / sizeof*(x)) -#define endof(x) ((x) + elemof(x)) - +#define elemof(x) (sizeof(x) / sizeof *(x)) +#define endof(x) ((x) + elemof(x)) diff --git a/src/whowatch.h b/src/whowatch.h index 52b67e6..9303ba2 100644 --- a/src/whowatch.h +++ b/src/whowatch.h @@ -19,122 +19,136 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include <stdio.h> -#include <errno.h> +#include "kbd.h" +#include "list.h" +#include "var.h" +#include <assert.h> +#include <ctype.h> +#include <curses.h> #include <err.h> -#include <stdlib.h> -#include <unistd.h> +#include <errno.h> #include <fcntl.h> -#include <utmp.h> -#include <string.h> +#include <regex.h> #include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> #include <sys/stat.h> -#include <sys/types.h> #include <sys/time.h> +#include <sys/types.h> #include <time.h> -#include <sys/ioctl.h> -#include <curses.h> -#include <ctype.h> -#include <assert.h> -#include <regex.h> -#include "list.h" -#include "kbd.h" -#include "var.h" +#include <unistd.h> +#include <utmpx.h> -#define WIN_HIDDEN(w) (!w->wrefresh) +#define WIN_HIDDEN(w) (!w->wrefresh) -#define CURSOR_COLOR A_REVERSE -#define NORMAL_COLOR A_NORMAL -#define CMD_COLUMN 52 +#define CURSOR_COLOR A_REVERSE +#define NORMAL_COLOR A_NORMAL +#define CMD_COLUMN 52 /* wdgt flags*/ -#define WDGT_NO_YRESIZE 1 /* prevents from Y-resizing */ -#define WDGT_CRSR_SND 2 /* propagate crsr changes */ +#define WDGT_NO_YRESIZE 1 /* prevents from Y-resizing */ +#define WDGT_CRSR_SND 2 /* propagate crsr changes */ #define WDGT_NEED_REDRAW 4 -#define WNEED_REDRAW(w) (w->flags |= WDGT_NEED_REDRAW) +#define WNEED_REDRAW(w) (w->flags |= WDGT_NEED_REDRAW) -#define KEY_SKIPPED 0 -#define KEY_HANDLED 1 -#define KEY_FINAL 2 +#define KEY_SKIPPED 0 +#define KEY_HANDLED 1 +#define KEY_FINAL 2 -#define MWANT_DSOURCE 0 -#define MWANT_CRSR_VAL 1 -#define MCUR_CRSR 2 -#define MCUR_HLP 3 -#define MWANT_UPID 4 -#define MALL_CRSR_REG 5 /* wdgt wants to receive cursor changes */ -#define MALL_CRSR_UNREG 6 /* doesn't want to receive it anymore */ -#define MSND_ESOURCE 7 -#define MSND_INFO 8 -#define MSND_SEARCH 9 -#define MSND_SEARCH_END 10 +#define MWANT_DSOURCE 0 +#define MWANT_CRSR_VAL 1 +#define MCUR_CRSR 2 +#define MCUR_HLP 3 +#define MWANT_UPID 4 +#define MALL_CRSR_REG 5 /* wdgt wants to receive cursor changes */ +#define MALL_CRSR_UNREG 6 /* doesn't want to receive it anymore */ +#define MSND_ESOURCE 7 +#define MSND_INFO 8 +#define MSND_SEARCH 9 +#define MSND_SEARCH_END 10 -#define CWND(w) ((WINDOW*)(w->wd)) /* ncurses window descriptor */ +#define CWND(w) ((WINDOW *)(w->wd)) /* ncurses window descriptor */ -#define INIT_PID 1 -#define real_line_nr(x,y) ((x) - (y)->offset) +#define INIT_PID 1 +#define real_line_nr(x, y) ((x) - (y)->offset) /* wdgts colors - correspond to curses COLOR_PAIR(n) */ -#define CLR_WHITE_BLACK 3 -#define CLR_BLACK_CYAN 8 -#define CLR_CYAN_BLACK 1 -#define CLR_RED_CYAN 7 -#define CLR_BLACK_WHITE 9 - - - -enum key { ENTER=0x100, UP, DOWN, LEFT, RIGHT, DELETE, ESC, CTRL_K, - CTRL_I, PG_DOWN, PG_UP, HOME, END, BACKSPACE, TAB }; +#define CLR_WHITE_BLACK 3 +#define CLR_BLACK_CYAN 8 +#define CLR_CYAN_BLACK 1 +#define CLR_RED_CYAN 7 +#define CLR_BLACK_WHITE 9 + +enum key { + ENTER = 0x100, + UP, + DOWN, + LEFT, + RIGHT, + DELETE, + ESC, + CTRL_K, + CTRL_I, + PG_DOWN, + PG_UP, + HOME, + END, + BACKSPACE, + TAB +}; extern unsigned long long ticks; extern int full_cmd; struct wdgt; struct win { - struct list_head wdgts; /* list of all widgets */ - struct list_head msg; /* message bus for wdgts communication */ - u32 sy, sx; /* physical screen coords (size+1) */ - u8 gbuf[256]; /* global buf to be used by any wdgt */ - u32 gbsize; - void *(*cval)(void); /* returns current cursor value (pid/name) */ - u8 need_redraw; /* immediate redraw needed for all wdgts*/ -// struct wdgt *mwdgt; + struct list_head wdgts; /* list of all widgets */ + struct list_head msg; /* message bus for wdgts communication */ + u32 sy, sx; /* physical screen coords (size+1) */ + u8 gbuf[256]; /* global buf to be used by any wdgt */ + u32 gbsize; + void *(*cval)(void); /* returns current cursor value (pid/name) */ + u8 need_redraw; /* immediate redraw needed for all wdgts*/ + // struct wdgt *mwdgt; }; struct wdgt { - struct list_head wdgts_l; - struct list_head msg_l; - struct win *mwin; - u8 flags; /* NO_YRESIZE, and for future use */ - char name[8]; /* debugging identification */ - void *prv; /* for use by widget itself */ - void (*wrefresh)(struct wdgt *); /* refreshes widget */ - void (*redraw)(struct wdgt *); /* print output into the widget */ - int (*keyh)(struct wdgt *, int); /* widget's key handler */ - void (*periodic)(struct wdgt *); /* to execute every step seconds */ - /* message handler */ - void *(*msgh)(struct wdgt *, int, struct wdgt *, void *); - u32 x, y, xsize, ysize, vx, vy; /* screen coords and offsets */ - u32 pysize, pxsize; /* pad real dimension */ - void *wd; /* screen window/pad descriptor */ - int crsr; /* current cursor position */ - u8 color; /* fg and bg colors, CLR_* macros */ - int nlines; /* total nr of lines in output */ - void *decor; /* frame around widget, if any */ -// u32 *crsrcache; /* holds pid/uid indexed by crsr line */ + struct list_head wdgts_l; + struct list_head msg_l; + struct win *mwin; + u8 flags; /* NO_YRESIZE, and for future use */ + char name[8]; /* debugging identification */ + void *prv; /* for use by widget itself */ + void (*wrefresh)(struct wdgt *); /* refreshes widget */ + void (*redraw)(struct wdgt *); /* print output into the widget + */ + int (*keyh)(struct wdgt *, int); /* widget's key handler */ + void (*periodic)(struct wdgt *); /* to execute every step seconds */ + /* message handler */ + void *(*msgh)(struct wdgt *, int, struct wdgt *, void *); + u32 x, y, xsize, ysize, vx, vy; /* screen coords and offsets */ + u32 pysize, pxsize; /* pad real dimension */ + void *wd; /* screen window/pad descriptor */ + int crsr; /* current cursor position */ + u8 color; /* fg and bg colors, CLR_* macros */ + int nlines; /* total nr of lines in output */ + void *decor; /* frame around widget, if any */ + // u32 *crsrcache; /* holds pid/uid indexed by crsr + // line */ }; -struct process -{ - struct process **prev; - struct process *next; - struct list_head plist_l; - int line; - int uid; - struct proc_t *proc; +struct process { + struct process **prev; + struct process *next; + struct list_head plist_l; + int line; + int uid; + struct proc_t *proc; }; -void err_exit(int , char *); +void err_exit(int, char *); void mwin_redraw_on(struct wdgt *); void mwin_refresh_on(struct wdgt *); void mwin_periodic_on(struct wdgt *); @@ -143,23 +157,23 @@ void mwin_need_redraw(struct wdgt *); void *wmsg_send(struct wdgt *, int, void *); /* screen.c */ void scr_doupdate(void); -void *scr_newwin(u32 ,u32 , u32 , u32); +void *scr_newwin(u32, u32, u32, u32); void scr_wdirty(struct wdgt *); -int scr_addfstr(struct wdgt *, char *, u32 , u32 ); +int scr_addfstr(struct wdgt *, char *, u32, u32); void scr_addstr(struct wdgt *, char *, u32); -void scr_maddstr(struct wdgt *, char *, u32 , u32, u32); +void scr_maddstr(struct wdgt *, char *, u32, u32, u32); void scr_werase(struct wdgt *); int scr_keyh(struct wdgt *, int); void scr_ldeleted(struct wdgt *, int); void scr_linserted(struct wdgt *, int); void scr_output_start(struct wdgt *); void scr_output_end(struct wdgt *); -void scr_wresize(struct wdgt *, u32 , u32 ); -void scr_attr_set(struct wdgt *, int ); -void scr_clr_set(struct wdgt *, int ); +void scr_wresize(struct wdgt *, u32, u32); +void scr_attr_set(struct wdgt *, int); +void scr_clr_set(struct wdgt *, int); void scr_wrefresh(struct wdgt *w); void scr_decor_resize(struct wdgt *w); -void scr_delline(struct wdgt *, u32 ); +void scr_delline(struct wdgt *, u32); void scr_box(struct wdgt *w, char *s, u8 c); void scr_crsr_jmp(struct wdgt *w, int l); @@ -185,15 +199,12 @@ void curses_init(); void curses_end(); /* proctree.c */ -int update_tree(void del(void*)); +int update_tree(void del(void *)); /* plist.c */ void delete_tree_line(void *line); /* procinfo.c */ -#ifdef HAVE_PROCESS_SYSCTL -int get_login_pid(char *tty); -#endif char *get_cmdline(int); int get_ppid(int); char *get_name(int); @@ -201,7 +212,7 @@ char *get_w(int pid); char *count_idle(char *tty); int proc_pid_uid(u32); #ifndef HAVE_GETLOADAVG -int proc_getloadavg(double [], int); +int proc_getloadavg(double[], int); #endif /* owner.c */ @@ -230,4 +241,3 @@ int getkey(); /* screen.c */ void term_raw(); - -- 2.52.0
