1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2004 Dag-Erling Coïdan 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 * $FreeBSD$ 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <err.h> 37 #include <grp.h> 38 #include <pwd.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <time.h> 43 #include <unistd.h> 44 45 struct xpasswd { 46 char *pw_name; 47 char *pw_passwd; 48 uid_t pw_uid; 49 gid_t pw_gid; 50 time_t pw_change; 51 char *pw_class; 52 char *pw_gecos; 53 char *pw_dir; 54 char *pw_shell; 55 time_t pw_expire; 56 int pw_selected; 57 }; 58 59 struct xgroup { 60 char *gr_name; 61 char *gr_passwd; 62 gid_t gr_gid; 63 char *gr_mem; 64 }; 65 66 static int everything = 1; 67 static int a_flag; 68 static int d_flag; 69 static const char *g_args; 70 static const char *l_args; 71 static int m_flag; 72 static int o_flag; 73 static int p_flag; 74 static int s_flag; 75 static int t_flag; 76 static int u_flag; 77 static int x_flag; 78 79 static int 80 member(const char *elem, const char *list) 81 { 82 char *p; 83 int len; 84 85 p = strstr(list, elem); 86 len = strlen(elem); 87 88 return (p != NULL && 89 (p == list || p[-1] == ',') && 90 (p[len] == '\0' || p[len] == ',')); 91 } 92 93 static void * 94 xmalloc(size_t size) 95 { 96 void *newptr; 97 98 if ((newptr = malloc(size)) == NULL) 99 err(1, "malloc()"); 100 return (newptr); 101 } 102 103 static void * 104 xrealloc(void *ptr, size_t size) 105 { 106 void *newptr; 107 108 if ((newptr = realloc(ptr, size)) == NULL) 109 err(1, "realloc()"); 110 return (newptr); 111 } 112 113 static char * 114 xstrdup(const char *str) 115 { 116 char *dupstr; 117 118 if ((dupstr = strdup(str)) == NULL) 119 err(1, "strdup()"); 120 return (dupstr); 121 } 122 123 static struct xgroup *grps; 124 static size_t grpsz; 125 static size_t ngrps; 126 127 static void 128 get_groups(void) 129 { 130 struct group *grp; 131 size_t len; 132 int i; 133 134 setgrent(); 135 for (;;) { 136 if (ngrps == grpsz) { 137 grpsz += grpsz ? grpsz : 128; 138 grps = xrealloc(grps, grpsz * sizeof *grps); 139 } 140 if ((grp = getgrent()) == NULL) 141 break; 142 grps[ngrps].gr_name = xstrdup(grp->gr_name); 143 grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd); 144 grps[ngrps].gr_gid = grp->gr_gid; 145 grps[ngrps].gr_mem = xstrdup(""); 146 for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i) 147 len += strlen(grp->gr_mem[i]) + 1; 148 grps[ngrps].gr_mem = xmalloc(len); 149 for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i) 150 len += sprintf(grps[ngrps].gr_mem + len, 151 i ? ",%s" : "%s", grp->gr_mem[i]); 152 grps[ngrps].gr_mem[len] = '\0'; 153 ngrps++; 154 } 155 endgrent(); 156 } 157 158 static struct xgroup * 159 find_group_bygid(gid_t gid) 160 { 161 unsigned int i; 162 163 for (i = 0; i < ngrps; ++i) 164 if (grps[i].gr_gid == gid) 165 return (&grps[i]); 166 return (NULL); 167 } 168 169 #if 0 170 static struct xgroup * 171 find_group_byname(const char *name) 172 { 173 unsigned int i; 174 175 for (i = 0; i < ngrps; ++i) 176 if (strcmp(grps[i].gr_name, name) == 0) 177 return (&grps[i]); 178 return (NULL); 179 } 180 #endif 181 182 static struct xpasswd *pwds; 183 static size_t pwdsz; 184 static size_t npwds; 185 186 static int 187 pwd_cmp_byname(const void *ap, const void *bp) 188 { 189 const struct passwd *a = ap; 190 const struct passwd *b = bp; 191 192 return (strcmp(a->pw_name, b->pw_name)); 193 } 194 195 static int 196 pwd_cmp_byuid(const void *ap, const void *bp) 197 { 198 const struct passwd *a = ap; 199 const struct passwd *b = bp; 200 201 return (a->pw_uid - b->pw_uid); 202 } 203 204 static void 205 get_users(void) 206 { 207 struct passwd *pwd; 208 209 setpwent(); 210 for (;;) { 211 if (npwds == pwdsz) { 212 pwdsz += pwdsz ? pwdsz : 128; 213 pwds = xrealloc(pwds, pwdsz * sizeof *pwds); 214 } 215 if ((pwd = getpwent()) == NULL) 216 break; 217 pwds[npwds].pw_name = xstrdup(pwd->pw_name); 218 pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd); 219 pwds[npwds].pw_uid = pwd->pw_uid; 220 pwds[npwds].pw_gid = pwd->pw_gid; 221 pwds[npwds].pw_change = pwd->pw_change; 222 pwds[npwds].pw_class = xstrdup(pwd->pw_class); 223 pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos); 224 pwds[npwds].pw_dir = xstrdup(pwd->pw_dir); 225 pwds[npwds].pw_shell = xstrdup(pwd->pw_shell); 226 pwds[npwds].pw_expire = pwd->pw_expire; 227 pwds[npwds].pw_selected = 0; 228 npwds++; 229 } 230 endpwent(); 231 } 232 233 static void 234 select_users(void) 235 { 236 unsigned int i, j; 237 struct xgroup *grp; 238 struct xpasswd *pwd; 239 240 for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) { 241 if (everything) { 242 pwd->pw_selected = 1; 243 continue; 244 } 245 if (d_flag) 246 if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) || 247 (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) { 248 pwd->pw_selected = 1; 249 continue; 250 } 251 if (g_args) { 252 for (j = 0, grp = grps; j < ngrps; ++j, ++grp) { 253 if (member(grp->gr_name, g_args) && 254 member(pwd->pw_name, grp->gr_mem)) { 255 pwd->pw_selected = 1; 256 break; 257 } 258 } 259 if (pwd->pw_selected) 260 continue; 261 } 262 if (l_args) 263 if (member(pwd->pw_name, l_args)) { 264 pwd->pw_selected = 1; 265 continue; 266 } 267 if (p_flag) 268 if (pwd->pw_passwd[0] == '\0') { 269 pwd->pw_selected = 1; 270 continue; 271 } 272 if (s_flag) 273 if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) { 274 pwd->pw_selected = 1; 275 continue; 276 } 277 if (u_flag) 278 if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) { 279 pwd->pw_selected = 1; 280 continue; 281 } 282 } 283 } 284 285 static void 286 sort_users(void) 287 { 288 if (t_flag) 289 mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname); 290 else 291 mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid); 292 } 293 294 static void 295 display_user(struct xpasswd *pwd) 296 { 297 struct xgroup *grp; 298 unsigned int i; 299 char cbuf[16], ebuf[16]; 300 struct tm *tm; 301 302 grp = find_group_bygid(pwd->pw_gid); 303 printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n", 304 pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "", 305 (long)pwd->pw_gid, pwd->pw_gecos); 306 if (m_flag) { 307 for (i = 0, grp = grps; i < ngrps; ++i, ++grp) { 308 if (grp->gr_gid == pwd->pw_gid || 309 !member(pwd->pw_name, grp->gr_mem)) 310 continue; 311 printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n", 312 "", grp->gr_name, (long)grp->gr_gid); 313 } 314 } 315 if (x_flag) { 316 printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir); 317 printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell); 318 } 319 if (a_flag) { 320 tm = gmtime(&pwd->pw_change); 321 strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm); 322 tm = gmtime(&pwd->pw_expire); 323 strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm); 324 printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf); 325 } 326 if (o_flag) 327 printf("\n"); 328 } 329 330 static void 331 list_users(void) 332 { 333 struct xpasswd *pwd; 334 unsigned int i; 335 336 for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) 337 if (pwd->pw_selected) 338 display_user(pwd); 339 } 340 341 static void 342 usage(void) 343 { 344 fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n"); 345 exit(1); 346 } 347 348 int 349 main(int argc, char * const argv[]) 350 { 351 int o; 352 353 while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1) 354 switch (o) { 355 case 'a': 356 a_flag = 1; 357 break; 358 case 'd': 359 everything = 0; 360 d_flag = 1; 361 break; 362 case 'g': 363 everything = 0; 364 g_args = optarg; 365 break; 366 case 'l': 367 everything = 0; 368 l_args = optarg; 369 break; 370 case 'm': 371 m_flag = 1; 372 break; 373 case 'o': 374 o_flag = 1; 375 break; 376 case 'p': 377 everything = 0; 378 p_flag = 1; 379 break; 380 case 's': 381 everything = 0; 382 s_flag = 1; 383 break; 384 case 't': 385 t_flag = 1; 386 break; 387 case 'u': 388 everything = 0; 389 u_flag = 1; 390 break; 391 case 'x': 392 x_flag = 1; 393 break; 394 default: 395 usage(); 396 } 397 398 argc -= optind; 399 argv += optind; 400 401 if (argc > 0) 402 usage(); 403 404 get_groups(); 405 get_users(); 406 select_users(); 407 sort_users(); 408 list_users(); 409 exit(0); 410 } 411