/*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2004 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> #include <err.h> #include <grp.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> struct xpasswd { char *pw_name; char *pw_passwd; uid_t pw_uid; gid_t pw_gid; time_t pw_change; char *pw_class; char *pw_gecos; char *pw_dir; char *pw_shell; time_t pw_expire; int pw_selected; }; struct xgroup { char *gr_name; char *gr_passwd; gid_t gr_gid; char *gr_mem; }; static int everything = 1; static int a_flag; static int d_flag; static const char *g_args; static const char *l_args; static int m_flag; static int o_flag; static int p_flag; static int s_flag; static int t_flag; static int u_flag; static int x_flag; static int member(const char *elem, const char *list) { char *p; int len; p = strstr(list, elem); len = strlen(elem); return (p != NULL && (p == list || p[-1] == ',') && (p[len] == '\0' || p[len] == ',')); } static void * xmalloc(size_t size) { void *newptr; if ((newptr = malloc(size)) == NULL) err(1, "malloc()"); return (newptr); } static void * xrealloc(void *ptr, size_t size) { void *newptr; if ((newptr = realloc(ptr, size)) == NULL) err(1, "realloc()"); return (newptr); } static char * xstrdup(const char *str) { char *dupstr; if ((dupstr = strdup(str)) == NULL) err(1, "strdup()"); return (dupstr); } static struct xgroup *grps; static size_t grpsz; static size_t ngrps; static void get_groups(void) { struct group *grp; size_t len; int i; setgrent(); for (;;) { if (ngrps == grpsz) { grpsz += grpsz ? grpsz : 128; grps = xrealloc(grps, grpsz * sizeof *grps); } if ((grp = getgrent()) == NULL) break; grps[ngrps].gr_name = xstrdup(grp->gr_name); grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); grps[ngrps].gr_gid = grp->gr_gid; grps[ngrps].gr_mem = xstrdup(""); for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) len += strlen(grp->gr_mem[i]) + 1; grps[ngrps].gr_mem = xmalloc(len); for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) len += sprintf(grps[ngrps].gr_mem + len, i ? ",%s" : "%s", grp->gr_mem[i]); grps[ngrps].gr_mem[len] = '\0'; ngrps++; } endgrent(); } static struct xgroup * find_group_bygid(gid_t gid) { unsigned int i; for (i = 0; i < ngrps; ++i) if (grps[i].gr_gid == gid) return (&grps[i]); return (NULL); } #if 0 static struct xgroup * find_group_byname(const char *name) { unsigned int i; for (i = 0; i < ngrps; ++i) if (strcmp(grps[i].gr_name, name) == 0) return (&grps[i]); return (NULL); } #endif static struct xpasswd *pwds; static size_t pwdsz; static size_t npwds; static int pwd_cmp_byname(const void *ap, const void *bp) { const struct passwd *a = ap; const struct passwd *b = bp; return (strcmp(a->pw_name, b->pw_name)); } static int pwd_cmp_byuid(const void *ap, const void *bp) { const struct passwd *a = ap; const struct passwd *b = bp; return (a->pw_uid - b->pw_uid); } static void get_users(void) { struct passwd *pwd; setpwent(); for (;;) { if (npwds == pwdsz) { pwdsz += pwdsz ? pwdsz : 128; pwds = xrealloc(pwds, pwdsz * sizeof *pwds); } if ((pwd = getpwent()) == NULL) break; pwds[npwds].pw_name = xstrdup(pwd->pw_name); pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); pwds[npwds].pw_uid = pwd->pw_uid; pwds[npwds].pw_gid = pwd->pw_gid; pwds[npwds].pw_change = pwd->pw_change; pwds[npwds].pw_class = xstrdup(pwd->pw_class); pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); pwds[npwds].pw_expire = pwd->pw_expire; pwds[npwds].pw_selected = 0; npwds++; } endpwent(); } static void select_users(void) { unsigned int i, j; struct xgroup *grp; struct xpasswd *pwd; for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { if (everything) { pwd->pw_selected = 1; continue; } if (d_flag) if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { pwd->pw_selected = 1; continue; } if (g_args) { for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { if (member(grp->gr_name, g_args) && member(pwd->pw_name, grp->gr_mem)) { pwd->pw_selected = 1; break; } } if (pwd->pw_selected) continue; } if (l_args) if (member(pwd->pw_name, l_args)) { pwd->pw_selected = 1; continue; } if (p_flag) if (pwd->pw_passwd[0] == '\0') { pwd->pw_selected = 1; continue; } if (s_flag) if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { pwd->pw_selected = 1; continue; } if (u_flag) if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { pwd->pw_selected = 1; continue; } } } static void sort_users(void) { if (t_flag) mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); else mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); } static void display_user(struct xpasswd *pwd) { struct xgroup *grp; unsigned int i; char cbuf[16], ebuf[16]; struct tm *tm; grp = find_group_bygid(pwd->pw_gid); printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", (long)pwd->pw_gid, pwd->pw_gecos); if (m_flag) { for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { if (grp->gr_gid == pwd->pw_gid || !member(pwd->pw_name, grp->gr_mem)) continue; printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", "", grp->gr_name, (long)grp->gr_gid); } } if (x_flag) { printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); } if (a_flag) { tm = gmtime(&pwd->pw_change); strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); tm = gmtime(&pwd->pw_expire); strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); } if (o_flag) printf("\n"); } static void list_users(void) { struct xpasswd *pwd; unsigned int i; for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) if (pwd->pw_selected) display_user(pwd); } static void usage(void) { fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); exit(1); } int main(int argc, char * const argv[]) { int o; while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) switch (o) { case 'a': a_flag = 1; break; case 'd': everything = 0; d_flag = 1; break; case 'g': everything = 0; g_args = optarg; break; case 'l': everything = 0; l_args = optarg; break; case 'm': m_flag = 1; break; case 'o': o_flag = 1; break; case 'p': everything = 0; p_flag = 1; break; case 's': everything = 0; s_flag = 1; break; case 't': t_flag = 1; break; case 'u': everything = 0; u_flag = 1; break; case 'x': x_flag = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) usage(); get_groups(); get_users(); select_users(); sort_users(); list_users(); exit(0); }