1126700Sdes/*-
2330449Seadler * SPDX-License-Identifier: BSD-3-Clause
3330449Seadler *
4228991Suqs * Copyright (c) 2004 Dag-Erling Co��dan Sm��rgrav
5126700Sdes * All rights reserved.
6126700Sdes *
7126700Sdes * Redistribution and use in source and binary forms, with or without
8126700Sdes * modification, are permitted provided that the following conditions
9126700Sdes * are met:
10126700Sdes * 1. Redistributions of source code must retain the above copyright
11126700Sdes *    notice, this list of conditions and the following disclaimer
12126700Sdes *    in this position and unchanged.
13126700Sdes * 2. Redistributions in binary form must reproduce the above copyright
14126700Sdes *    notice, this list of conditions and the following disclaimer in the
15126700Sdes *    documentation and/or other materials provided with the distribution.
16126700Sdes * 3. The name of the author may not be used to endorse or promote products
17126700Sdes *    derived from this software without specific prior written permission.
18126700Sdes *
19126700Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20126700Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21126700Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22126700Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23126700Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24126700Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25126700Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26126700Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27126700Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28126700Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29126700Sdes *
30126700Sdes * $FreeBSD: stable/11/usr.bin/logins/logins.c 330449 2018-03-05 07:26:05Z eadler $
31126700Sdes */
32126700Sdes
33126700Sdes#include <sys/cdefs.h>
34126700Sdes__FBSDID("$FreeBSD: stable/11/usr.bin/logins/logins.c 330449 2018-03-05 07:26:05Z eadler $");
35126700Sdes
36126700Sdes#include <err.h>
37126700Sdes#include <grp.h>
38126700Sdes#include <pwd.h>
39126700Sdes#include <stdio.h>
40126700Sdes#include <stdlib.h>
41126700Sdes#include <string.h>
42126700Sdes#include <time.h>
43126700Sdes#include <unistd.h>
44126700Sdes
45126700Sdesstruct xpasswd {
46126700Sdes	char		*pw_name;
47126700Sdes	char		*pw_passwd;
48126700Sdes	uid_t		 pw_uid;
49126700Sdes	gid_t		 pw_gid;
50126700Sdes	time_t		 pw_change;
51126700Sdes	char		*pw_class;
52126700Sdes	char		*pw_gecos;
53126700Sdes	char		*pw_dir;
54126700Sdes	char		*pw_shell;
55126700Sdes	time_t		 pw_expire;
56126700Sdes	int		 pw_selected;
57126700Sdes};
58126700Sdes
59126700Sdesstruct xgroup {
60126700Sdes	char		*gr_name;
61126700Sdes	char		*gr_passwd;
62126700Sdes	gid_t		 gr_gid;
63126700Sdes	char		*gr_mem;
64126700Sdes};
65126700Sdes
66126700Sdesstatic int		 everything = 1;
67126700Sdesstatic int		 a_flag;
68126700Sdesstatic int		 d_flag;
69126700Sdesstatic const char	*g_args;
70126700Sdesstatic const char	*l_args;
71126700Sdesstatic int		 m_flag;
72126700Sdesstatic int		 o_flag;
73126700Sdesstatic int		 p_flag;
74126700Sdesstatic int		 s_flag;
75126700Sdesstatic int		 t_flag;
76126700Sdesstatic int		 u_flag;
77126700Sdesstatic int		 x_flag;
78126700Sdes
79126700Sdesstatic int
80126700Sdesmember(const char *elem, const char *list)
81126700Sdes{
82126700Sdes	char *p;
83126700Sdes	int len;
84126700Sdes
85126700Sdes	p = strstr(list, elem);
86126700Sdes	len = strlen(elem);
87126700Sdes
88126700Sdes	return (p != NULL &&
89126700Sdes	    (p == list || p[-1] == ',') &&
90126700Sdes	    (p[len] == '\0' || p[len] == ','));
91126700Sdes}
92126700Sdes
93126700Sdesstatic void *
94126700Sdesxmalloc(size_t size)
95126700Sdes{
96126700Sdes	void *newptr;
97126700Sdes
98126700Sdes	if ((newptr = malloc(size)) == NULL)
99126700Sdes		err(1, "malloc()");
100126700Sdes	return (newptr);
101126700Sdes}
102126700Sdes
103126700Sdesstatic void *
104126700Sdesxrealloc(void *ptr, size_t size)
105126700Sdes{
106126700Sdes	void *newptr;
107126700Sdes
108126700Sdes	if ((newptr = realloc(ptr, size)) == NULL)
109126700Sdes		err(1, "realloc()");
110126700Sdes	return (newptr);
111126700Sdes}
112126700Sdes
113126700Sdesstatic char *
114126700Sdesxstrdup(const char *str)
115126700Sdes{
116126700Sdes	char *dupstr;
117126700Sdes
118126700Sdes	if ((dupstr = strdup(str)) == NULL)
119126700Sdes		err(1, "strdup()");
120126700Sdes	return (dupstr);
121126700Sdes}
122126700Sdes
123126700Sdesstatic struct xgroup	*grps;
124126700Sdesstatic size_t		 grpsz;
125126700Sdesstatic size_t		 ngrps;
126126700Sdes
127126700Sdesstatic void
128126700Sdesget_groups(void)
129126700Sdes{
130126700Sdes	struct group *grp;
131126700Sdes	size_t len;
132126700Sdes	int i;
133126700Sdes
134126700Sdes	setgrent();
135126700Sdes	for (;;) {
136126700Sdes		if (ngrps == grpsz) {
137126700Sdes			grpsz += grpsz ? grpsz : 128;
138126700Sdes			grps = xrealloc(grps, grpsz * sizeof *grps);
139126700Sdes		}
140126700Sdes		if ((grp = getgrent()) == NULL)
141126700Sdes			break;
142126700Sdes		grps[ngrps].gr_name = xstrdup(grp->gr_name);
143126700Sdes		grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd);
144126700Sdes		grps[ngrps].gr_gid = grp->gr_gid;
145126700Sdes		grps[ngrps].gr_mem = xstrdup("");
146126700Sdes		for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i)
147126700Sdes			len += strlen(grp->gr_mem[i]) + 1;
148126700Sdes		grps[ngrps].gr_mem = xmalloc(len);
149126700Sdes		for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i)
150126700Sdes			len += sprintf(grps[ngrps].gr_mem + len,
151126700Sdes			    i ? ",%s" : "%s", grp->gr_mem[i]);
152126700Sdes		grps[ngrps].gr_mem[len] = '\0';
153126700Sdes		ngrps++;
154126700Sdes	}
155126700Sdes	endgrent();
156126700Sdes}
157126700Sdes
158126700Sdesstatic struct xgroup *
159126700Sdesfind_group_bygid(gid_t gid)
160126700Sdes{
161126700Sdes	unsigned int i;
162126700Sdes
163126700Sdes	for (i = 0; i < ngrps; ++i)
164126700Sdes		if (grps[i].gr_gid == gid)
165126700Sdes			return (&grps[i]);
166126700Sdes	return (NULL);
167126700Sdes}
168126700Sdes
169126700Sdes#if 0
170126700Sdesstatic struct xgroup *
171126700Sdesfind_group_byname(const char *name)
172126700Sdes{
173126700Sdes	unsigned int i;
174126700Sdes
175126700Sdes	for (i = 0; i < ngrps; ++i)
176126700Sdes		if (strcmp(grps[i].gr_name, name) == 0)
177126700Sdes			return (&grps[i]);
178126700Sdes	return (NULL);
179126700Sdes}
180126700Sdes#endif
181126700Sdes
182126700Sdesstatic struct xpasswd	*pwds;
183126700Sdesstatic size_t		 pwdsz;
184126700Sdesstatic size_t		 npwds;
185126700Sdes
186126700Sdesstatic int
187126700Sdespwd_cmp_byname(const void *ap, const void *bp)
188126700Sdes{
189126700Sdes	const struct passwd *a = ap;
190126700Sdes	const struct passwd *b = bp;
191126700Sdes
192126700Sdes	return (strcmp(a->pw_name, b->pw_name));
193126700Sdes}
194126700Sdes
195126700Sdesstatic int
196126700Sdespwd_cmp_byuid(const void *ap, const void *bp)
197126700Sdes{
198126700Sdes	const struct passwd *a = ap;
199126700Sdes	const struct passwd *b = bp;
200126700Sdes
201126700Sdes	return (a->pw_uid - b->pw_uid);
202126700Sdes}
203126700Sdes
204126700Sdesstatic void
205126700Sdesget_users(void)
206126700Sdes{
207126700Sdes	struct passwd *pwd;
208126700Sdes
209126700Sdes	setpwent();
210126700Sdes	for (;;) {
211126700Sdes		if (npwds == pwdsz) {
212126700Sdes			pwdsz += pwdsz ? pwdsz : 128;
213126700Sdes			pwds = xrealloc(pwds, pwdsz * sizeof *pwds);
214126700Sdes		}
215126700Sdes		if ((pwd = getpwent()) == NULL)
216126700Sdes			break;
217126700Sdes		pwds[npwds].pw_name = xstrdup(pwd->pw_name);
218126700Sdes		pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd);
219126700Sdes		pwds[npwds].pw_uid = pwd->pw_uid;
220126700Sdes		pwds[npwds].pw_gid = pwd->pw_gid;
221126700Sdes		pwds[npwds].pw_change = pwd->pw_change;
222126700Sdes		pwds[npwds].pw_class = xstrdup(pwd->pw_class);
223126700Sdes		pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos);
224126700Sdes		pwds[npwds].pw_dir = xstrdup(pwd->pw_dir);
225126700Sdes		pwds[npwds].pw_shell = xstrdup(pwd->pw_shell);
226126700Sdes		pwds[npwds].pw_expire = pwd->pw_expire;
227126700Sdes		pwds[npwds].pw_selected = 0;
228126700Sdes		npwds++;
229126700Sdes	}
230126700Sdes	endpwent();
231126700Sdes}
232126700Sdes
233126700Sdesstatic void
234126700Sdesselect_users(void)
235126700Sdes{
236126700Sdes	unsigned int i, j;
237126700Sdes	struct xgroup *grp;
238126700Sdes	struct xpasswd *pwd;
239126700Sdes
240126700Sdes	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) {
241126700Sdes		if (everything) {
242126700Sdes			pwd->pw_selected = 1;
243126700Sdes			continue;
244126700Sdes		}
245126700Sdes		if (d_flag)
246126700Sdes			if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) ||
247126700Sdes			    (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) {
248126700Sdes				pwd->pw_selected = 1;
249126700Sdes				continue;
250126700Sdes			}
251126700Sdes		if (g_args) {
252126700Sdes			for (j = 0, grp = grps; j < ngrps; ++j, ++grp) {
253126700Sdes				if (member(grp->gr_name, g_args) &&
254126700Sdes				    member(pwd->pw_name, grp->gr_mem)) {
255126700Sdes					pwd->pw_selected = 1;
256126700Sdes					break;
257126700Sdes				}
258126700Sdes			}
259126700Sdes			if (pwd->pw_selected)
260126700Sdes				continue;
261126700Sdes		}
262126700Sdes		if (l_args)
263126700Sdes			if (member(pwd->pw_name, l_args)) {
264126700Sdes				pwd->pw_selected = 1;
265126700Sdes				continue;
266126700Sdes			}
267126700Sdes		if (p_flag)
268126700Sdes			if (pwd->pw_passwd[0] == '\0') {
269126700Sdes				pwd->pw_selected = 1;
270126700Sdes				continue;
271126700Sdes			}
272126700Sdes		if (s_flag)
273126700Sdes			if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) {
274126700Sdes				pwd->pw_selected = 1;
275126700Sdes				continue;
276126700Sdes			}
277126700Sdes		if (u_flag)
278126700Sdes			if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) {
279126700Sdes				pwd->pw_selected = 1;
280126700Sdes				continue;
281126700Sdes			}
282126700Sdes	}
283126700Sdes}
284126700Sdes
285126700Sdesstatic void
286126700Sdessort_users(void)
287126700Sdes{
288126700Sdes	if (t_flag)
289126700Sdes		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname);
290126700Sdes	else
291126700Sdes		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid);
292126700Sdes}
293126700Sdes
294126700Sdesstatic void
295126700Sdesdisplay_user(struct xpasswd *pwd)
296126700Sdes{
297126700Sdes	struct xgroup *grp;
298126700Sdes	unsigned int i;
299126700Sdes	char cbuf[16], ebuf[16];
300126700Sdes	struct tm *tm;
301126700Sdes
302126700Sdes	grp = find_group_bygid(pwd->pw_gid);
303126700Sdes	printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n",
304126700Sdes	    pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "",
305126700Sdes	    (long)pwd->pw_gid, pwd->pw_gecos);
306126700Sdes	if (m_flag) {
307126700Sdes		for (i = 0, grp = grps; i < ngrps; ++i, ++grp) {
308126700Sdes			if (grp->gr_gid == pwd->pw_gid ||
309126700Sdes			    !member(pwd->pw_name, grp->gr_mem))
310126700Sdes				continue;
311126700Sdes			printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n",
312126700Sdes			    "", grp->gr_name, (long)grp->gr_gid);
313126700Sdes		}
314126700Sdes	}
315126700Sdes	if (x_flag) {
316126700Sdes		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir);
317126700Sdes		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell);
318126700Sdes	}
319126700Sdes	if (a_flag) {
320126700Sdes		tm = gmtime(&pwd->pw_change);
321126700Sdes		strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm);
322126700Sdes		tm = gmtime(&pwd->pw_expire);
323126700Sdes		strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm);
324126700Sdes		printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf);
325126700Sdes	}
326126700Sdes	if (o_flag)
327126700Sdes		printf("\n");
328126700Sdes}
329126700Sdes
330126700Sdesstatic void
331126700Sdeslist_users(void)
332126700Sdes{
333126700Sdes	struct xpasswd *pwd;
334126700Sdes	unsigned int i;
335126700Sdes
336126700Sdes	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd)
337126700Sdes		if (pwd->pw_selected)
338126700Sdes			display_user(pwd);
339126700Sdes}
340126700Sdes
341126700Sdesstatic void
342126700Sdesusage(void)
343126700Sdes{
344126700Sdes	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
345126700Sdes	exit(1);
346126700Sdes}
347126700Sdes
348126700Sdesint
349126700Sdesmain(int argc, char * const argv[])
350126700Sdes{
351126700Sdes	int o;
352126700Sdes
353126700Sdes	while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1)
354126700Sdes		switch (o) {
355126700Sdes		case 'a':
356126700Sdes			a_flag = 1;
357126700Sdes			break;
358126700Sdes		case 'd':
359126700Sdes			everything = 0;
360126700Sdes			d_flag = 1;
361126700Sdes			break;
362126700Sdes		case 'g':
363126700Sdes			everything = 0;
364126700Sdes			g_args = optarg;
365126700Sdes			break;
366126700Sdes		case 'l':
367126700Sdes			everything = 0;
368126700Sdes			l_args = optarg;
369126700Sdes			break;
370126700Sdes		case 'm':
371126700Sdes			m_flag = 1;
372126700Sdes			break;
373126700Sdes		case 'o':
374126700Sdes			o_flag = 1;
375126700Sdes			break;
376126700Sdes		case 'p':
377126700Sdes			everything = 0;
378126700Sdes			p_flag = 1;
379126700Sdes			break;
380126700Sdes		case 's':
381126700Sdes			everything = 0;
382126700Sdes			s_flag = 1;
383126700Sdes			break;
384126700Sdes		case 't':
385126700Sdes			t_flag = 1;
386126700Sdes			break;
387126700Sdes		case 'u':
388126700Sdes			everything = 0;
389126700Sdes			u_flag = 1;
390126700Sdes			break;
391126700Sdes		case 'x':
392126700Sdes			x_flag = 1;
393126700Sdes			break;
394126700Sdes		default:
395126700Sdes			usage();
396126700Sdes		}
397126700Sdes
398126700Sdes	argc -= optind;
399126700Sdes	argv += optind;
400126700Sdes
401126700Sdes	if (argc > 0)
402126700Sdes		usage();
403126700Sdes
404126700Sdes	get_groups();
405126700Sdes	get_users();
406126700Sdes	select_users();
407126700Sdes	sort_users();
408126700Sdes	list_users();
409126700Sdes	exit(0);
410126700Sdes}
411