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