133965Sjdp/*- 278828Sobrien * Copyright (c) 2004 Dag-Erling Co��dan Sm��rgrav 3218822Sdim * All rights reserved. 438889Sjdp * 533965Sjdp * Redistribution and use in source and binary forms, with or without 633965Sjdp * modification, are permitted provided that the following conditions 7218822Sdim * are met: 833965Sjdp * 1. Redistributions of source code must retain the above copyright 9218822Sdim * notice, this list of conditions and the following disclaimer 10218822Sdim * in this position and unchanged. 11218822Sdim * 2. Redistributions in binary form must reproduce the above copyright 12218822Sdim * notice, this list of conditions and the following disclaimer in the 1333965Sjdp * documentation and/or other materials provided with the distribution. 14218822Sdim * 3. The name of the author may not be used to endorse or promote products 15218822Sdim * derived from this software without specific prior written permission. 16218822Sdim * 17218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1833965Sjdp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19218822Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20218822Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21218822Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2233965Sjdp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23218822Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2433965Sjdp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2533965Sjdp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26218822Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2777298Sobrien * 2833965Sjdp * $FreeBSD$ 2933965Sjdp */ 3033965Sjdp 3133965Sjdp#include <sys/cdefs.h> 3233965Sjdp__FBSDID("$FreeBSD$"); 3333965Sjdp 3433965Sjdp#include <err.h> 3533965Sjdp#include <grp.h> 3633965Sjdp#include <pwd.h> 3733965Sjdp#include <stdio.h> 3833965Sjdp#include <stdlib.h> 3933965Sjdp#include <string.h> 4033965Sjdp#include <time.h> 4133965Sjdp#include <unistd.h> 4233965Sjdp 4333965Sjdpstruct xpasswd { 4433965Sjdp char *pw_name; 4577298Sobrien char *pw_passwd; 46218822Sdim uid_t pw_uid; 4733965Sjdp gid_t pw_gid; 4877298Sobrien time_t pw_change; 49218822Sdim char *pw_class; 5033965Sjdp char *pw_gecos; 5133965Sjdp char *pw_dir; 5233965Sjdp char *pw_shell; 5333965Sjdp time_t pw_expire; 5433965Sjdp int pw_selected; 5533965Sjdp}; 5633965Sjdp 5733965Sjdpstruct xgroup { 5833965Sjdp char *gr_name; 5933965Sjdp char *gr_passwd; 6033965Sjdp gid_t gr_gid; 6133965Sjdp char *gr_mem; 6233965Sjdp}; 6333965Sjdp 6477298Sobrienstatic int everything = 1; 6533965Sjdpstatic int a_flag; 6633965Sjdpstatic int d_flag; 6733965Sjdpstatic const char *g_args; 6833965Sjdpstatic const char *l_args; 6933965Sjdpstatic int m_flag; 7033965Sjdpstatic int o_flag; 7133965Sjdpstatic int p_flag; 7233965Sjdpstatic int s_flag; 7333965Sjdpstatic int t_flag; 74218822Sdimstatic int u_flag; 7533965Sjdpstatic int x_flag; 7633965Sjdp 77218822Sdimstatic int 7833965Sjdpmember(const char *elem, const char *list) 7933965Sjdp{ 8033965Sjdp char *p; 8133965Sjdp int len; 8233965Sjdp 8333965Sjdp p = strstr(list, elem); 8433965Sjdp len = strlen(elem); 8533965Sjdp 8633965Sjdp return (p != NULL && 8733965Sjdp (p == list || p[-1] == ',') && 8833965Sjdp (p[len] == '\0' || p[len] == ',')); 89218822Sdim} 9033965Sjdp 9133965Sjdpstatic void * 9233965Sjdpxmalloc(size_t size) 9333965Sjdp{ 9433965Sjdp void *newptr; 9533965Sjdp 9633965Sjdp if ((newptr = malloc(size)) == NULL) 9733965Sjdp err(1, "malloc()"); 9833965Sjdp return (newptr); 9933965Sjdp} 100218822Sdim 10133965Sjdpstatic void * 102218822Sdimxrealloc(void *ptr, size_t size) 10377298Sobrien{ 104218822Sdim void *newptr; 10533965Sjdp 10633965Sjdp if ((newptr = realloc(ptr, size)) == NULL) 10733965Sjdp err(1, "realloc()"); 10889857Sobrien return (newptr); 10933965Sjdp} 11033965Sjdp 11177298Sobrienstatic char * 11233965Sjdpxstrdup(const char *str) 11389857Sobrien{ 11433965Sjdp char *dupstr; 115218822Sdim 116218822Sdim if ((dupstr = strdup(str)) == NULL) 11733965Sjdp err(1, "strdup()"); 11833965Sjdp return (dupstr); 11933965Sjdp} 12033965Sjdp 121218822Sdimstatic struct xgroup *grps; 12233965Sjdpstatic size_t grpsz; 12394536Sobrienstatic size_t ngrps; 12433965Sjdp 12533965Sjdpstatic void 12677298Sobrienget_groups(void) 12777298Sobrien{ 12877298Sobrien struct group *grp; 12994536Sobrien size_t len; 13077298Sobrien int i; 13177298Sobrien 13277298Sobrien setgrent(); 13377298Sobrien for (;;) { 13477298Sobrien if (ngrps == grpsz) { 13577298Sobrien grpsz += grpsz ? grpsz : 128; 13677298Sobrien grps = xrealloc(grps, grpsz * sizeof *grps); 137130561Sobrien } 13877298Sobrien if ((grp = getgrent()) == NULL) 13933965Sjdp break; 14033965Sjdp grps[ngrps].gr_name = xstrdup(grp->gr_name); 14133965Sjdp grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); 14233965Sjdp grps[ngrps].gr_gid = grp->gr_gid; 14333965Sjdp grps[ngrps].gr_mem = xstrdup(""); 14433965Sjdp for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) 14533965Sjdp len += strlen(grp->gr_mem[i]) + 1; 14633965Sjdp grps[ngrps].gr_mem = xmalloc(len); 14733965Sjdp for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) 14833965Sjdp len += sprintf(grps[ngrps].gr_mem + len, 14933965Sjdp i ? ",%s" : "%s", grp->gr_mem[i]); 15033965Sjdp grps[ngrps].gr_mem[len] = '\0'; 15133965Sjdp ngrps++; 15233965Sjdp } 15333965Sjdp endgrent(); 15433965Sjdp} 15533965Sjdp 15633965Sjdpstatic struct xgroup * 15733965Sjdpfind_group_bygid(gid_t gid) 15833965Sjdp{ 15933965Sjdp unsigned int i; 16033965Sjdp 16133965Sjdp for (i = 0; i < ngrps; ++i) 16233965Sjdp if (grps[i].gr_gid == gid) 16333965Sjdp return (&grps[i]); 16433965Sjdp return (NULL); 16533965Sjdp} 16633965Sjdp 16733965Sjdp#if 0 16833965Sjdpstatic struct xgroup * 16933965Sjdpfind_group_byname(const char *name) 17033965Sjdp{ 17133965Sjdp unsigned int i; 17233965Sjdp 17333965Sjdp for (i = 0; i < ngrps; ++i) 17433965Sjdp if (strcmp(grps[i].gr_name, name) == 0) 17533965Sjdp return (&grps[i]); 176218822Sdim return (NULL); 17733965Sjdp} 178218822Sdim#endif 179218822Sdim 180218822Sdimstatic struct xpasswd *pwds; 181218822Sdimstatic size_t pwdsz; 182218822Sdimstatic size_t npwds; 183218822Sdim 184218822Sdimstatic int 185218822Sdimpwd_cmp_byname(const void *ap, const void *bp) 186218822Sdim{ 187218822Sdim const struct passwd *a = ap; 188218822Sdim const struct passwd *b = bp; 189218822Sdim 19033965Sjdp return (strcmp(a->pw_name, b->pw_name)); 19133965Sjdp} 192130561Sobrien 193218822Sdimstatic int 19433965Sjdppwd_cmp_byuid(const void *ap, const void *bp) 195104834Sobrien{ 196104834Sobrien const struct passwd *a = ap; 19733965Sjdp const struct passwd *b = bp; 19833965Sjdp 199218822Sdim return (a->pw_uid - b->pw_uid); 200104834Sobrien} 201130561Sobrien 202130561Sobrienstatic void 203104834Sobrienget_users(void) 204104834Sobrien{ 205104834Sobrien struct passwd *pwd; 206104834Sobrien 207104834Sobrien setpwent(); 208104834Sobrien for (;;) { 209104834Sobrien if (npwds == pwdsz) { 21033965Sjdp pwdsz += pwdsz ? pwdsz : 128; 211130561Sobrien pwds = xrealloc(pwds, pwdsz * sizeof *pwds); 21233965Sjdp } 21333965Sjdp if ((pwd = getpwent()) == NULL) 21433965Sjdp break; 21533965Sjdp pwds[npwds].pw_name = xstrdup(pwd->pw_name); 21633965Sjdp pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); 21733965Sjdp pwds[npwds].pw_uid = pwd->pw_uid; 218218822Sdim pwds[npwds].pw_gid = pwd->pw_gid; 21933965Sjdp pwds[npwds].pw_change = pwd->pw_change; 22033965Sjdp pwds[npwds].pw_class = xstrdup(pwd->pw_class); 22133965Sjdp pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); 22289857Sobrien pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); 22333965Sjdp pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); 22433965Sjdp pwds[npwds].pw_expire = pwd->pw_expire; 225130561Sobrien pwds[npwds].pw_selected = 0; 22633965Sjdp npwds++; 22733965Sjdp } 22833965Sjdp endpwent(); 22933965Sjdp} 23033965Sjdp 23133965Sjdpstatic void 23233965Sjdpselect_users(void) 23333965Sjdp{ 23433965Sjdp unsigned int i, j; 23533965Sjdp struct xgroup *grp; 23633965Sjdp struct xpasswd *pwd; 237218822Sdim 238218822Sdim for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { 239218822Sdim if (everything) { 240218822Sdim pwd->pw_selected = 1; 24133965Sjdp continue; 24233965Sjdp } 24333965Sjdp if (d_flag) 24433965Sjdp if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || 24533965Sjdp (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { 24633965Sjdp pwd->pw_selected = 1; 24733965Sjdp continue; 24833965Sjdp } 24933965Sjdp if (g_args) { 25033965Sjdp for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { 25189857Sobrien if (member(grp->gr_name, g_args) && 25233965Sjdp member(pwd->pw_name, grp->gr_mem)) { 25333965Sjdp pwd->pw_selected = 1; 25433965Sjdp break; 25533965Sjdp } 25633965Sjdp } 25733965Sjdp if (pwd->pw_selected) 25833965Sjdp continue; 259218822Sdim } 260218822Sdim if (l_args) 26133965Sjdp if (member(pwd->pw_name, l_args)) { 26233965Sjdp pwd->pw_selected = 1; 26333965Sjdp continue; 26433965Sjdp } 26533965Sjdp if (p_flag) 26633965Sjdp if (pwd->pw_passwd[0] == '\0') { 267130561Sobrien pwd->pw_selected = 1; 268218822Sdim continue; 26933965Sjdp } 27033965Sjdp if (s_flag) 27133965Sjdp if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { 272218822Sdim pwd->pw_selected = 1; 27333965Sjdp continue; 274130561Sobrien } 27533965Sjdp if (u_flag) 27633965Sjdp if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { 27733965Sjdp pwd->pw_selected = 1; 27833965Sjdp continue; 27933965Sjdp } 28033965Sjdp } 28133965Sjdp} 28233965Sjdp 28333965Sjdpstatic void 28433965Sjdpsort_users(void) 28533965Sjdp{ 28633965Sjdp if (t_flag) 28733965Sjdp mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); 288130561Sobrien else 28933965Sjdp mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); 29033965Sjdp} 29133965Sjdp 29233965Sjdpstatic void 29333965Sjdpdisplay_user(struct xpasswd *pwd) 294130561Sobrien{ 295218822Sdim struct xgroup *grp; 29633965Sjdp unsigned int i; 29733965Sjdp char cbuf[16], ebuf[16]; 29833965Sjdp struct tm *tm; 299130561Sobrien 30033965Sjdp grp = find_group_bygid(pwd->pw_gid); 30133965Sjdp printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", 30233965Sjdp pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", 30333965Sjdp (long)pwd->pw_gid, pwd->pw_gecos); 30433965Sjdp if (m_flag) { 30533965Sjdp for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { 30633965Sjdp if (grp->gr_gid == pwd->pw_gid || 30733965Sjdp !member(pwd->pw_name, grp->gr_mem)) 30833965Sjdp continue; 30933965Sjdp printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", 31033965Sjdp "", grp->gr_name, (long)grp->gr_gid); 311130561Sobrien } 31233965Sjdp } 31333965Sjdp if (x_flag) { 31433965Sjdp printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); 31533965Sjdp printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); 31633965Sjdp } 31733965Sjdp if (a_flag) { 31833965Sjdp tm = gmtime(&pwd->pw_change); 31933965Sjdp strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); 32033965Sjdp tm = gmtime(&pwd->pw_expire); 32133965Sjdp strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); 32233965Sjdp printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); 32333965Sjdp } 32433965Sjdp if (o_flag) 32533965Sjdp printf("\n"); 32633965Sjdp} 32733965Sjdp 32833965Sjdpstatic void 32933965Sjdplist_users(void) 33033965Sjdp{ 33133965Sjdp struct xpasswd *pwd; 33233965Sjdp unsigned int i; 33333965Sjdp 33433965Sjdp for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) 33533965Sjdp if (pwd->pw_selected) 33633965Sjdp display_user(pwd); 33733965Sjdp} 33833965Sjdp 33933965Sjdpstatic void 34033965Sjdpusage(void) 34133965Sjdp{ 34233965Sjdp fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); 34333965Sjdp exit(1); 34433965Sjdp} 34589857Sobrien 34633965Sjdpint 34733965Sjdpmain(int argc, char * const argv[]) 34833965Sjdp{ 34933965Sjdp int o; 35033965Sjdp 35133965Sjdp while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) 35233965Sjdp switch (o) { 35333965Sjdp case 'a': 35460484Sobrien a_flag = 1; 35533965Sjdp break; 35633965Sjdp case 'd': 35733965Sjdp everything = 0; 35833965Sjdp d_flag = 1; 35933965Sjdp break; 36033965Sjdp case 'g': 36133965Sjdp everything = 0; 36233965Sjdp g_args = optarg; 36333965Sjdp break; 364218822Sdim case 'l': 36533965Sjdp everything = 0; 36633965Sjdp l_args = optarg; 36733965Sjdp break; 36833965Sjdp case 'm': 36933965Sjdp m_flag = 1; 37033965Sjdp break; 37133965Sjdp case 'o': 37289857Sobrien o_flag = 1; 37333965Sjdp break; 37489857Sobrien case 'p': 37533965Sjdp everything = 0; 37633965Sjdp p_flag = 1; 37733965Sjdp break; 37833965Sjdp case 's': 379218822Sdim everything = 0; 38033965Sjdp s_flag = 1; 38133965Sjdp break; 38233965Sjdp case 't': 38333965Sjdp t_flag = 1; 38433965Sjdp break; 38533965Sjdp case 'u': 38633965Sjdp everything = 0; 38733965Sjdp u_flag = 1; 38833965Sjdp break; 38933965Sjdp case 'x': 39033965Sjdp x_flag = 1; 39133965Sjdp break; 39233965Sjdp default: 39333965Sjdp usage(); 39433965Sjdp } 39533965Sjdp 39689857Sobrien argc -= optind; 39733965Sjdp argv += optind; 39833965Sjdp 39933965Sjdp if (argc > 0) 40033965Sjdp usage(); 40133965Sjdp 40233965Sjdp get_groups(); 40333965Sjdp get_users(); 40433965Sjdp select_users(); 40533965Sjdp sort_users(); 40633965Sjdp list_users(); 40733965Sjdp exit(0); 40833965Sjdp} 40933965Sjdp