/* * wwho.c * * 2003-10-29 Fredrik Roubert * 2004-02-26 Fredrik Roubert * 2004-09-08 Fredrik Roubert * */ #if defined(sun) && ! defined(__EXTENSIONS__) # define __EXTENSIONS__ #endif #include #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) # define HAS_GETUTENT 0 #else # define HAS_GETUTENT 1 #endif #if ! HAS_GETUTENT || defined(ultrix) # define HAS_UTMPX 0 #else # define HAS_UTMPX 1 #endif #if HAS_UTMPX # include typedef struct utmpx utmp_t; #else # include typedef struct utmp utmp_t; #endif #define ERROR(function) \ fprintf(stderr, "%s: %i: %s(): %s\n", \ __FILE__, __LINE__, #function, strerror(errno)) #if defined(ultrix) extern void setutent(void); extern void endutent(void); extern struct utmp *getutent(void); extern int ioctl(int, int, void *); #endif #if ! HAS_GETUTENT static int fd = -1; static void setutent(void) { if (fd == -1 && (fd = open(_PATH_UTMP, O_RDONLY)) == -1) ERROR(open); } static void endutent(void) { if (fd != -1 && close(fd) == -1) ERROR(close); } static utmp_t * getutent(void) { static utmp_t ut; ssize_t l; if (fd == -1) return NULL; if ((l = read(fd, &ut, sizeof (utmp_t))) != sizeof (utmp_t)) { if (l == -1) ERROR(read); return NULL; } return &ut; } #endif static int fputcx(int c, FILE *stream, size_t n) { size_t i; flockfile(stream); for (i = 0; i < n; i ++) if (putc_unlocked(c, stream) == EOF) { funlockfile(stream); return EOF; } funlockfile(stream); return (unsigned char)c; } #if ! HAS_UTMPX && ! defined(__GNU_LIBRARY__) static size_t strnlen(const char *s, size_t maxlen) { size_t i; for (i = 0; i < maxlen && s[i] != '\0'; i ++) ; return i; } #endif static char * strdupx(const char *s, size_t len) { char *tmp; if ((tmp = malloc(len + 1)) == NULL) return NULL; memcpy(tmp, s, len); tmp[len] = '\0'; return tmp; } struct list { void **data; size_t cur, max; }; struct user { size_t name_len, gecos_len; char *name, *gecos; struct list *hosts; }; static struct list * createlist(size_t max) { struct list *list; if ((list = malloc(sizeof (struct list))) == NULL) { ERROR(malloc); return NULL; } if ((list->data = malloc(max * sizeof (void *))) == NULL) { ERROR(malloc); free(list); return NULL; } list->cur = 0; list->max = max; return list; } static int growlist(struct list *list) { struct list *tmp; size_t max; max = list->max << 1; if ((tmp = realloc(list->data, max * sizeof (void *))) == NULL) { ERROR(realloc); return 0; } list->max = max; list->data = (void **)tmp; return 1; } static void freelist(struct list *list, void (*freedata)(void *)) { size_t i; for (i = 0; i < list->cur; i ++) (*freedata)(list->data[i]); free(list->data); free(list); } static int addhost(struct list *hosts, utmp_t *ut) { char **host; size_t i; #if defined(sun) if (ut->ut_syslen < 1) #else if (ut->ut_host[0] == '\0') #endif return 1; for (i = 0; i < hosts->cur; i ++) if (! strncmp(hosts->data[i], ut->ut_host, sizeof (ut->ut_host))) return 1; if (hosts->cur == hosts->max && ! growlist(hosts)) return 0; host = (char **)hosts->data + hosts->cur; #if HAS_UTMPX # if defined(sun) if ((*host = strdupx(ut->ut_host, ut->ut_syslen)) == NULL) { ERROR(strdupx); return 0; } # else if ((*host = strdup(ut->ut_host)) == NULL) { ERROR(strdup); return 0; } # endif #else if ((*host = strdupx(ut->ut_host, strnlen(ut->ut_host, sizeof (ut->ut_host)))) == NULL) { ERROR(strdupx); return 0; } #endif hosts->cur ++; return 1; } static int adduser(struct list *users, utmp_t *ut) { struct user *user; struct passwd *pwd; const char *name, *data; size_t i; #if HAS_UTMPX name = ut->ut_user; #else name = ut->ut_name; #endif for (i = 0; i < users->cur; i ++) { user = users->data[i]; if (! strcmp(user->name, name)) return addhost(user->hosts, ut); } if ((pwd = getpwnam(name)) == NULL) { ERROR(getpwnam); goto error; } if ((user = malloc(sizeof (struct user))) == NULL) { ERROR(malloc); goto error; } user->name_len = strlen(name); if ((user->name = strdupx(name, user->name_len)) == NULL) { ERROR(strdupx); goto error_user; } user->gecos_len = (data = strchr(pwd->pw_gecos, ',')) != NULL ? data - pwd->pw_gecos : strlen(pwd->pw_gecos); if ((user->gecos = strdupx(pwd->pw_gecos, user->gecos_len)) == NULL) { ERROR(strdupx); goto error_name; } if ((user->hosts = createlist(2)) == NULL) goto error_gecos; if (users->cur == users->max && ! growlist(users)) goto error_hosts; if (! addhost(user->hosts, ut)) goto error_hosts; users->data[users->cur ++] = user; return 1; error_hosts: freelist(user->hosts, NULL); error_gecos: free(user->gecos); error_name: free(user->name); error_user: free(user); error: return 0; } #if DEBUG static void freeuser(void *ptr) { struct user *user = ptr; freelist(user->hosts, free); free(user->gecos); free(user->name); free(user); } #endif static int cmpuser(const void *a, const void *b) { return strcmp((*(struct user **)a)->name, (*(struct user **)b)->name); } static struct list * readutmp(void) { struct list *users; utmp_t *ut; if ((users = createlist(8)) == NULL) return NULL; #if HAS_UTMPX setutxent(); while ((ut = getutxent()) != NULL) if (ut->ut_type == USER_PROCESS && ! adduser(users, ut)) break; endutxent(); #else setutent(); while ((ut = getutent()) != NULL) if (ut->ut_name[0] != '\0' && ! adduser(users, ut)) break; endutent(); #endif qsort(users->data, users->cur, sizeof (void *), cmpuser); return users; } static void printutmp(struct list *users, unsigned short width) { unsigned short name_max, gecos_max, hosts_max; struct user *user; char *host; size_t i, j, k, l; name_max = 0; gecos_max = 0; for (i = 0; i < users->cur; i ++) { user = users->data[i]; if (user->name_len > name_max) name_max = user->name_len; if (user->gecos_len > gecos_max) gecos_max = user->gecos_len; } hosts_max = width - name_max - 2 - gecos_max - 2 - 2; for (i = 0; i < users->cur; i ++) { user = users->data[i]; fwrite(user->name, 1, user->name_len, stdout); fputcx(' ', stdout, name_max - user->name_len + 2); fwrite(user->gecos, 1, user->gecos_len, stdout); if (user->hosts->cur > 0) { fputcx(' ', stdout, gecos_max - user->gecos_len + 2); putchar('('); for (j = 0, k = 0;; ) { host = user->hosts->data[j]; l = strlen(host); if (k + l > hosts_max) l = hosts_max - k; fwrite(host, 1, l, stdout); k += l; if (++ j == user->hosts->cur || ++ k >= hosts_max) break; putchar(','); } putchar(')'); } putchar('\n'); } } static struct winsize * getwinsize(void) { static struct winsize size = { 25, 80 }; errno = 0; if (isatty(STDOUT_FILENO)) { if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) { ERROR(ioctl); return NULL; } } else { if (errno != 0) { ERROR(isatty); return NULL; } } return &size; } int main(int argc, char *argv[]) { struct winsize *size; struct list *users; if ((size = getwinsize()) == NULL) return EXIT_FAILURE; if ((users = readutmp()) == NULL) return EXIT_FAILURE; printutmp(users, size->ws_col); #if DEBUG freelist(users, freeuser); #endif return EXIT_SUCCESS; }