1126700Sdes/*- 2126700Sdes * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav 3126700Sdes * All rights reserved. 4126700Sdes * 5126700Sdes * Redistribution and use in source and binary forms, with or without 6126700Sdes * modification, are permitted provided that the following conditions 7126700Sdes * are met: 8126700Sdes * 1. Redistributions of source code must retain the above copyright 9126700Sdes * notice, this list of conditions and the following disclaimer 10126700Sdes * in this position and unchanged. 11126700Sdes * 2. Redistributions in binary form must reproduce the above copyright 12126700Sdes * notice, this list of conditions and the following disclaimer in the 13126700Sdes * documentation and/or other materials provided with the distribution. 14126700Sdes * 3. The name of the author may not be used to endorse or promote products 15126700Sdes * derived from this software without specific prior written permission. 16126700Sdes * 17126700Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18126700Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19126700Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20126700Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21126700Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22126700Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23126700Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24126700Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25126700Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26126700Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27126700Sdes * 28126700Sdes * $FreeBSD$ 29126700Sdes */ 30126700Sdes 31126700Sdes#include <sys/cdefs.h> 32126700Sdes__FBSDID("$FreeBSD$"); 33126700Sdes 34126700Sdes#include <err.h> 35126700Sdes#include <grp.h> 36126700Sdes#include <pwd.h> 37126700Sdes#include <stdio.h> 38126700Sdes#include <stdlib.h> 39126700Sdes#include <string.h> 40126700Sdes#include <time.h> 41126700Sdes#include <unistd.h> 42126700Sdes 43126700Sdesstruct xpasswd { 44126700Sdes char *pw_name; 45126700Sdes char *pw_passwd; 46126700Sdes uid_t pw_uid; 47126700Sdes gid_t pw_gid; 48126700Sdes time_t pw_change; 49126700Sdes char *pw_class; 50126700Sdes char *pw_gecos; 51126700Sdes char *pw_dir; 52126700Sdes char *pw_shell; 53126700Sdes time_t pw_expire; 54126700Sdes int pw_selected; 55126700Sdes}; 56126700Sdes 57126700Sdesstruct xgroup { 58126700Sdes char *gr_name; 59126700Sdes char *gr_passwd; 60126700Sdes gid_t gr_gid; 61126700Sdes char *gr_mem; 62126700Sdes}; 63126700Sdes 64126700Sdesstatic int everything = 1; 65126700Sdesstatic int a_flag; 66126700Sdesstatic int d_flag; 67126700Sdesstatic const char *g_args; 68126700Sdesstatic const char *l_args; 69126700Sdesstatic int m_flag; 70126700Sdesstatic int o_flag; 71126700Sdesstatic int p_flag; 72126700Sdesstatic int s_flag; 73126700Sdesstatic int t_flag; 74126700Sdesstatic int u_flag; 75126700Sdesstatic int x_flag; 76126700Sdes 77126700Sdesstatic int 78126700Sdesmember(const char *elem, const char *list) 79126700Sdes{ 80126700Sdes char *p; 81126700Sdes int len; 82126700Sdes 83126700Sdes p = strstr(list, elem); 84126700Sdes len = strlen(elem); 85126700Sdes 86126700Sdes return (p != NULL && 87126700Sdes (p == list || p[-1] == ',') && 88126700Sdes (p[len] == '\0' || p[len] == ',')); 89126700Sdes} 90126700Sdes 91126700Sdesstatic void * 92126700Sdesxmalloc(size_t size) 93126700Sdes{ 94126700Sdes void *newptr; 95126700Sdes 96126700Sdes if ((newptr = malloc(size)) == NULL) 97126700Sdes err(1, "malloc()"); 98126700Sdes return (newptr); 99126700Sdes} 100126700Sdes 101126700Sdesstatic void * 102126700Sdesxrealloc(void *ptr, size_t size) 103126700Sdes{ 104126700Sdes void *newptr; 105126700Sdes 106126700Sdes if ((newptr = realloc(ptr, size)) == NULL) 107126700Sdes err(1, "realloc()"); 108126700Sdes return (newptr); 109126700Sdes} 110126700Sdes 111126700Sdesstatic char * 112126700Sdesxstrdup(const char *str) 113126700Sdes{ 114126700Sdes char *dupstr; 115126700Sdes 116126700Sdes if ((dupstr = strdup(str)) == NULL) 117126700Sdes err(1, "strdup()"); 118126700Sdes return (dupstr); 119126700Sdes} 120126700Sdes 121126700Sdesstatic struct xgroup *grps; 122126700Sdesstatic size_t grpsz; 123126700Sdesstatic size_t ngrps; 124126700Sdes 125126700Sdesstatic void 126126700Sdesget_groups(void) 127126700Sdes{ 128126700Sdes struct group *grp; 129126700Sdes size_t len; 130126700Sdes int i; 131126700Sdes 132126700Sdes setgrent(); 133126700Sdes for (;;) { 134126700Sdes if (ngrps == grpsz) { 135126700Sdes grpsz += grpsz ? grpsz : 128; 136126700Sdes grps = xrealloc(grps, grpsz * sizeof *grps); 137126700Sdes } 138126700Sdes if ((grp = getgrent()) == NULL) 139126700Sdes break; 140126700Sdes grps[ngrps].gr_name = xstrdup(grp->gr_name); 141126700Sdes grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); 142126700Sdes grps[ngrps].gr_gid = grp->gr_gid; 143126700Sdes grps[ngrps].gr_mem = xstrdup(""); 144126700Sdes for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) 145126700Sdes len += strlen(grp->gr_mem[i]) + 1; 146126700Sdes grps[ngrps].gr_mem = xmalloc(len); 147126700Sdes for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) 148126700Sdes len += sprintf(grps[ngrps].gr_mem + len, 149126700Sdes i ? ",%s" : "%s", grp->gr_mem[i]); 150126700Sdes grps[ngrps].gr_mem[len] = '\0'; 151126700Sdes ngrps++; 152126700Sdes } 153126700Sdes endgrent(); 154126700Sdes} 155126700Sdes 156126700Sdesstatic struct xgroup * 157126700Sdesfind_group_bygid(gid_t gid) 158126700Sdes{ 159126700Sdes unsigned int i; 160126700Sdes 161126700Sdes for (i = 0; i < ngrps; ++i) 162126700Sdes if (grps[i].gr_gid == gid) 163126700Sdes return (&grps[i]); 164126700Sdes return (NULL); 165126700Sdes} 166126700Sdes 167126700Sdes#if 0 168126700Sdesstatic struct xgroup * 169126700Sdesfind_group_byname(const char *name) 170126700Sdes{ 171126700Sdes unsigned int i; 172126700Sdes 173126700Sdes for (i = 0; i < ngrps; ++i) 174126700Sdes if (strcmp(grps[i].gr_name, name) == 0) 175126700Sdes return (&grps[i]); 176126700Sdes return (NULL); 177126700Sdes} 178126700Sdes#endif 179126700Sdes 180126700Sdesstatic struct xpasswd *pwds; 181126700Sdesstatic size_t pwdsz; 182126700Sdesstatic size_t npwds; 183126700Sdes 184126700Sdesstatic int 185126700Sdespwd_cmp_byname(const void *ap, const void *bp) 186126700Sdes{ 187126700Sdes const struct passwd *a = ap; 188126700Sdes const struct passwd *b = bp; 189126700Sdes 190126700Sdes return (strcmp(a->pw_name, b->pw_name)); 191126700Sdes} 192126700Sdes 193126700Sdesstatic int 194126700Sdespwd_cmp_byuid(const void *ap, const void *bp) 195126700Sdes{ 196126700Sdes const struct passwd *a = ap; 197126700Sdes const struct passwd *b = bp; 198126700Sdes 199126700Sdes return (a->pw_uid - b->pw_uid); 200126700Sdes} 201126700Sdes 202126700Sdesstatic void 203126700Sdesget_users(void) 204126700Sdes{ 205126700Sdes struct passwd *pwd; 206126700Sdes 207126700Sdes setpwent(); 208126700Sdes for (;;) { 209126700Sdes if (npwds == pwdsz) { 210126700Sdes pwdsz += pwdsz ? pwdsz : 128; 211126700Sdes pwds = xrealloc(pwds, pwdsz * sizeof *pwds); 212126700Sdes } 213126700Sdes if ((pwd = getpwent()) == NULL) 214126700Sdes break; 215126700Sdes pwds[npwds].pw_name = xstrdup(pwd->pw_name); 216126700Sdes pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); 217126700Sdes pwds[npwds].pw_uid = pwd->pw_uid; 218126700Sdes pwds[npwds].pw_gid = pwd->pw_gid; 219126700Sdes pwds[npwds].pw_change = pwd->pw_change; 220126700Sdes pwds[npwds].pw_class = xstrdup(pwd->pw_class); 221126700Sdes pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); 222126700Sdes pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); 223126700Sdes pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); 224126700Sdes pwds[npwds].pw_expire = pwd->pw_expire; 225126700Sdes pwds[npwds].pw_selected = 0; 226126700Sdes npwds++; 227126700Sdes } 228126700Sdes endpwent(); 229126700Sdes} 230126700Sdes 231126700Sdesstatic void 232126700Sdesselect_users(void) 233126700Sdes{ 234126700Sdes unsigned int i, j; 235126700Sdes struct xgroup *grp; 236126700Sdes struct xpasswd *pwd; 237126700Sdes 238126700Sdes for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { 239126700Sdes if (everything) { 240126700Sdes pwd->pw_selected = 1; 241126700Sdes continue; 242126700Sdes } 243126700Sdes if (d_flag) 244126700Sdes if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || 245126700Sdes (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { 246126700Sdes pwd->pw_selected = 1; 247126700Sdes continue; 248126700Sdes } 249126700Sdes if (g_args) { 250126700Sdes for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { 251126700Sdes if (member(grp->gr_name, g_args) && 252126700Sdes member(pwd->pw_name, grp->gr_mem)) { 253126700Sdes pwd->pw_selected = 1; 254126700Sdes break; 255126700Sdes } 256126700Sdes } 257126700Sdes if (pwd->pw_selected) 258126700Sdes continue; 259126700Sdes } 260126700Sdes if (l_args) 261126700Sdes if (member(pwd->pw_name, l_args)) { 262126700Sdes pwd->pw_selected = 1; 263126700Sdes continue; 264126700Sdes } 265126700Sdes if (p_flag) 266126700Sdes if (pwd->pw_passwd[0] == '\0') { 267126700Sdes pwd->pw_selected = 1; 268126700Sdes continue; 269126700Sdes } 270126700Sdes if (s_flag) 271126700Sdes if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { 272126700Sdes pwd->pw_selected = 1; 273126700Sdes continue; 274126700Sdes } 275126700Sdes if (u_flag) 276126700Sdes if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { 277126700Sdes pwd->pw_selected = 1; 278126700Sdes continue; 279126700Sdes } 280126700Sdes } 281126700Sdes} 282126700Sdes 283126700Sdesstatic void 284126700Sdessort_users(void) 285126700Sdes{ 286126700Sdes if (t_flag) 287126700Sdes mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); 288126700Sdes else 289126700Sdes mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); 290126700Sdes} 291126700Sdes 292126700Sdesstatic void 293126700Sdesdisplay_user(struct xpasswd *pwd) 294126700Sdes{ 295126700Sdes struct xgroup *grp; 296126700Sdes unsigned int i; 297126700Sdes char cbuf[16], ebuf[16]; 298126700Sdes struct tm *tm; 299126700Sdes 300126700Sdes grp = find_group_bygid(pwd->pw_gid); 301126700Sdes printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", 302126700Sdes pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", 303126700Sdes (long)pwd->pw_gid, pwd->pw_gecos); 304126700Sdes if (m_flag) { 305126700Sdes for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { 306126700Sdes if (grp->gr_gid == pwd->pw_gid || 307126700Sdes !member(pwd->pw_name, grp->gr_mem)) 308126700Sdes continue; 309126700Sdes printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", 310126700Sdes "", grp->gr_name, (long)grp->gr_gid); 311126700Sdes } 312126700Sdes } 313126700Sdes if (x_flag) { 314126700Sdes printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); 315126700Sdes printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); 316126700Sdes } 317126700Sdes if (a_flag) { 318126700Sdes tm = gmtime(&pwd->pw_change); 319126700Sdes strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); 320126700Sdes tm = gmtime(&pwd->pw_expire); 321126700Sdes strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); 322126700Sdes printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); 323126700Sdes } 324126700Sdes if (o_flag) 325126700Sdes printf("\n"); 326126700Sdes} 327126700Sdes 328126700Sdesstatic void 329126700Sdeslist_users(void) 330126700Sdes{ 331126700Sdes struct xpasswd *pwd; 332126700Sdes unsigned int i; 333126700Sdes 334126700Sdes for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) 335126700Sdes if (pwd->pw_selected) 336126700Sdes display_user(pwd); 337126700Sdes} 338126700Sdes 339126700Sdesstatic void 340126700Sdesusage(void) 341126700Sdes{ 342126700Sdes fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); 343126700Sdes exit(1); 344126700Sdes} 345126700Sdes 346126700Sdesint 347126700Sdesmain(int argc, char * const argv[]) 348126700Sdes{ 349126700Sdes int o; 350126700Sdes 351126700Sdes while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) 352126700Sdes switch (o) { 353126700Sdes case 'a': 354126700Sdes a_flag = 1; 355126700Sdes break; 356126700Sdes case 'd': 357126700Sdes everything = 0; 358126700Sdes d_flag = 1; 359126700Sdes break; 360126700Sdes case 'g': 361126700Sdes everything = 0; 362126700Sdes g_args = optarg; 363126700Sdes break; 364126700Sdes case 'l': 365126700Sdes everything = 0; 366126700Sdes l_args = optarg; 367126700Sdes break; 368126700Sdes case 'm': 369126700Sdes m_flag = 1; 370126700Sdes break; 371126700Sdes case 'o': 372126700Sdes o_flag = 1; 373126700Sdes break; 374126700Sdes case 'p': 375126700Sdes everything = 0; 376126700Sdes p_flag = 1; 377126700Sdes break; 378126700Sdes case 's': 379126700Sdes everything = 0; 380126700Sdes s_flag = 1; 381126700Sdes break; 382126700Sdes case 't': 383126700Sdes t_flag = 1; 384126700Sdes break; 385126700Sdes case 'u': 386126700Sdes everything = 0; 387126700Sdes u_flag = 1; 388126700Sdes break; 389126700Sdes case 'x': 390126700Sdes x_flag = 1; 391126700Sdes break; 392126700Sdes default: 393126700Sdes usage(); 394126700Sdes } 395126700Sdes 396126700Sdes argc -= optind; 397126700Sdes argv += optind; 398126700Sdes 399126700Sdes if (argc > 0) 400126700Sdes usage(); 401126700Sdes 402126700Sdes get_groups(); 403126700Sdes get_users(); 404126700Sdes select_users(); 405126700Sdes sort_users(); 406126700Sdes list_users(); 407126700Sdes exit(0); 408126700Sdes} 409