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