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