1/*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2004 Dag-Erling Sm��rgrav 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32#include <err.h> 33#include <grp.h> 34#include <pwd.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <time.h> 39#include <unistd.h> 40 41struct xpasswd { 42 char *pw_name; 43 char *pw_passwd; 44 uid_t pw_uid; 45 gid_t pw_gid; 46 time_t pw_change; 47 char *pw_class; 48 char *pw_gecos; 49 char *pw_dir; 50 char *pw_shell; 51 time_t pw_expire; 52 int pw_selected; 53}; 54 55struct xgroup { 56 char *gr_name; 57 char *gr_passwd; 58 gid_t gr_gid; 59 char *gr_mem; 60}; 61 62static int everything = 1; 63static int a_flag; 64static int d_flag; 65static const char *g_args; 66static const char *l_args; 67static int m_flag; 68static int o_flag; 69static int p_flag; 70static int s_flag; 71static int t_flag; 72static int u_flag; 73static int x_flag; 74 75static int 76member(const char *elem, const char *list) 77{ 78 char *p; 79 int len; 80 81 p = strstr(list, elem); 82 len = strlen(elem); 83 84 return (p != NULL && 85 (p == list || p[-1] == ',') && 86 (p[len] == '\0' || p[len] == ',')); 87} 88 89static void * 90xmalloc(size_t size) 91{ 92 void *newptr; 93 94 if ((newptr = malloc(size)) == NULL) 95 err(1, "malloc()"); 96 return (newptr); 97} 98 99static void * 100xrealloc(void *ptr, size_t size) 101{ 102 void *newptr; 103 104 if ((newptr = realloc(ptr, size)) == NULL) 105 err(1, "realloc()"); 106 return (newptr); 107} 108 109static char * 110xstrdup(const char *str) 111{ 112 char *dupstr; 113 114 if ((dupstr = strdup(str)) == NULL) 115 err(1, "strdup()"); 116 return (dupstr); 117} 118 119static struct xgroup *grps; 120static size_t grpsz; 121static size_t ngrps; 122 123static void 124get_groups(void) 125{ 126 struct group *grp; 127 size_t len; 128 int i; 129 130 setgrent(); 131 for (;;) { 132 if (ngrps == grpsz) { 133 grpsz += grpsz ? grpsz : 128; 134 grps = xrealloc(grps, grpsz * sizeof *grps); 135 } 136 if ((grp = getgrent()) == NULL) 137 break; 138 grps[ngrps].gr_name = xstrdup(grp->gr_name); 139 grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); 140 grps[ngrps].gr_gid = grp->gr_gid; 141 grps[ngrps].gr_mem = xstrdup(""); 142 for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) 143 len += strlen(grp->gr_mem[i]) + 1; 144 grps[ngrps].gr_mem = xmalloc(len); 145 for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) 146 len += sprintf(grps[ngrps].gr_mem + len, 147 i ? ",%s" : "%s", grp->gr_mem[i]); 148 grps[ngrps].gr_mem[len] = '\0'; 149 ngrps++; 150 } 151 endgrent(); 152} 153 154static struct xgroup * 155find_group_bygid(gid_t gid) 156{ 157 unsigned int i; 158 159 for (i = 0; i < ngrps; ++i) 160 if (grps[i].gr_gid == gid) 161 return (&grps[i]); 162 return (NULL); 163} 164 165#if 0 166static struct xgroup * 167find_group_byname(const char *name) 168{ 169 unsigned int i; 170 171 for (i = 0; i < ngrps; ++i) 172 if (strcmp(grps[i].gr_name, name) == 0) 173 return (&grps[i]); 174 return (NULL); 175} 176#endif 177 178static struct xpasswd *pwds; 179static size_t pwdsz; 180static size_t npwds; 181 182static int 183pwd_cmp_byname(const void *ap, const void *bp) 184{ 185 const struct passwd *a = ap; 186 const struct passwd *b = bp; 187 188 return (strcmp(a->pw_name, b->pw_name)); 189} 190 191static int 192pwd_cmp_byuid(const void *ap, const void *bp) 193{ 194 const struct passwd *a = ap; 195 const struct passwd *b = bp; 196 197 return (a->pw_uid - b->pw_uid); 198} 199 200static void 201get_users(void) 202{ 203 struct passwd *pwd; 204 205 setpwent(); 206 for (;;) { 207 if (npwds == pwdsz) { 208 pwdsz += pwdsz ? pwdsz : 128; 209 pwds = xrealloc(pwds, pwdsz * sizeof *pwds); 210 } 211 if ((pwd = getpwent()) == NULL) 212 break; 213 pwds[npwds].pw_name = xstrdup(pwd->pw_name); 214 pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); 215 pwds[npwds].pw_uid = pwd->pw_uid; 216 pwds[npwds].pw_gid = pwd->pw_gid; 217 pwds[npwds].pw_change = pwd->pw_change; 218 pwds[npwds].pw_class = xstrdup(pwd->pw_class); 219 pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); 220 pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); 221 pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); 222 pwds[npwds].pw_expire = pwd->pw_expire; 223 pwds[npwds].pw_selected = 0; 224 npwds++; 225 } 226 endpwent(); 227} 228 229static void 230select_users(void) 231{ 232 unsigned int i, j; 233 struct xgroup *grp; 234 struct xpasswd *pwd; 235 236 for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { 237 if (everything) { 238 pwd->pw_selected = 1; 239 continue; 240 } 241 if (d_flag) 242 if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || 243 (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { 244 pwd->pw_selected = 1; 245 continue; 246 } 247 if (g_args) { 248 for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { 249 if (member(grp->gr_name, g_args) && 250 member(pwd->pw_name, grp->gr_mem)) { 251 pwd->pw_selected = 1; 252 break; 253 } 254 } 255 if (pwd->pw_selected) 256 continue; 257 } 258 if (l_args) 259 if (member(pwd->pw_name, l_args)) { 260 pwd->pw_selected = 1; 261 continue; 262 } 263 if (p_flag) 264 if (pwd->pw_passwd[0] == '\0') { 265 pwd->pw_selected = 1; 266 continue; 267 } 268 if (s_flag) 269 if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { 270 pwd->pw_selected = 1; 271 continue; 272 } 273 if (u_flag) 274 if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { 275 pwd->pw_selected = 1; 276 continue; 277 } 278 } 279} 280 281static void 282sort_users(void) 283{ 284 if (t_flag) 285 mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); 286 else 287 mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); 288} 289 290static void 291display_user(struct xpasswd *pwd) 292{ 293 struct xgroup *grp; 294 unsigned int i; 295 char cbuf[16], ebuf[16]; 296 struct tm *tm; 297 298 grp = find_group_bygid(pwd->pw_gid); 299 printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", 300 pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", 301 (long)pwd->pw_gid, pwd->pw_gecos); 302 if (m_flag) { 303 for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { 304 if (grp->gr_gid == pwd->pw_gid || 305 !member(pwd->pw_name, grp->gr_mem)) 306 continue; 307 printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", 308 "", grp->gr_name, (long)grp->gr_gid); 309 } 310 } 311 if (x_flag) { 312 printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); 313 printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); 314 } 315 if (a_flag) { 316 tm = gmtime(&pwd->pw_change); 317 strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); 318 tm = gmtime(&pwd->pw_expire); 319 strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); 320 printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); 321 } 322 if (o_flag) 323 printf("\n"); 324} 325 326static void 327list_users(void) 328{ 329 struct xpasswd *pwd; 330 unsigned int i; 331 332 for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) 333 if (pwd->pw_selected) 334 display_user(pwd); 335} 336 337static void 338usage(void) 339{ 340 fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); 341 exit(1); 342} 343 344int 345main(int argc, char * const argv[]) 346{ 347 int o; 348 349 while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) 350 switch (o) { 351 case 'a': 352 a_flag = 1; 353 break; 354 case 'd': 355 everything = 0; 356 d_flag = 1; 357 break; 358 case 'g': 359 everything = 0; 360 g_args = optarg; 361 break; 362 case 'l': 363 everything = 0; 364 l_args = optarg; 365 break; 366 case 'm': 367 m_flag = 1; 368 break; 369 case 'o': 370 o_flag = 1; 371 break; 372 case 'p': 373 everything = 0; 374 p_flag = 1; 375 break; 376 case 's': 377 everything = 0; 378 s_flag = 1; 379 break; 380 case 't': 381 t_flag = 1; 382 break; 383 case 'u': 384 everything = 0; 385 u_flag = 1; 386 break; 387 case 'x': 388 x_flag = 1; 389 break; 390 default: 391 usage(); 392 } 393 394 argc -= optind; 395 argv += optind; 396 397 if (argc > 0) 398 usage(); 399 400 get_groups(); 401 get_users(); 402 select_users(); 403 sort_users(); 404 list_users(); 405 exit(0); 406} 407