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: stable/12/usr.bin/logins/logins.c 326276 2017-11-27 15:37:16Z pfg $
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: stable/12/usr.bin/logins/logins.c 326276 2017-11-27 15:37:16Z pfg $");
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
member(const char * elem,const char * list)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 *
xmalloc(size_t size)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 *
xrealloc(void * ptr,size_t size)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 *
xstrdup(const char * str)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
get_groups(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 *
find_group_bygid(gid_t gid)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
pwd_cmp_byname(const void * ap,const void * bp)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
pwd_cmp_byuid(const void * ap,const void * bp)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
get_users(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
select_users(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
sort_users(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
display_user(struct xpasswd * pwd)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
list_users(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
usage(void)342 usage(void)
343 {
344 fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
345 exit(1);
346 }
347
348 int
main(int argc,char * const argv[])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