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
45struct 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
59struct xgroup {
60	char		*gr_name;
61	char		*gr_passwd;
62	gid_t		 gr_gid;
63	char		*gr_mem;
64};
65
66static int		 everything = 1;
67static int		 a_flag;
68static int		 d_flag;
69static const char	*g_args;
70static const char	*l_args;
71static int		 m_flag;
72static int		 o_flag;
73static int		 p_flag;
74static int		 s_flag;
75static int		 t_flag;
76static int		 u_flag;
77static int		 x_flag;
78
79static int
80member(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
93static void *
94xmalloc(size_t size)
95{
96	void *newptr;
97
98	if ((newptr = malloc(size)) == NULL)
99		err(1, "malloc()");
100	return (newptr);
101}
102
103static void *
104xrealloc(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
113static char *
114xstrdup(const char *str)
115{
116	char *dupstr;
117
118	if ((dupstr = strdup(str)) == NULL)
119		err(1, "strdup()");
120	return (dupstr);
121}
122
123static struct xgroup	*grps;
124static size_t		 grpsz;
125static size_t		 ngrps;
126
127static void
128get_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
158static struct xgroup *
159find_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
170static struct xgroup *
171find_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
182static struct xpasswd	*pwds;
183static size_t		 pwdsz;
184static size_t		 npwds;
185
186static int
187pwd_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
195static int
196pwd_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
204static void
205get_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
233static void
234select_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
285static void
286sort_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
294static void
295display_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
330static void
331list_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
341static void
342usage(void)
343{
344	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
345	exit(1);
346}
347
348int
349main(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