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