1/*-
2 * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <err.h>
35#include <grp.h>
36#include <pwd.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41#include <unistd.h>
42
43struct xpasswd {
44	char		*pw_name;
45	char		*pw_passwd;
46	uid_t		 pw_uid;
47	gid_t		 pw_gid;
48	time_t		 pw_change;
49	char		*pw_class;
50	char		*pw_gecos;
51	char		*pw_dir;
52	char		*pw_shell;
53	time_t		 pw_expire;
54	int		 pw_selected;
55};
56
57struct xgroup {
58	char		*gr_name;
59	char		*gr_passwd;
60	gid_t		 gr_gid;
61	char		*gr_mem;
62};
63
64static int		 everything = 1;
65static int		 a_flag;
66static int		 d_flag;
67static const char	*g_args;
68static const char	*l_args;
69static int		 m_flag;
70static int		 o_flag;
71static int		 p_flag;
72static int		 s_flag;
73static int		 t_flag;
74static int		 u_flag;
75static int		 x_flag;
76
77static int
78member(const char *elem, const char *list)
79{
80	char *p;
81	int len;
82
83	p = strstr(list, elem);
84	len = strlen(elem);
85
86	return (p != NULL &&
87	    (p == list || p[-1] == ',') &&
88	    (p[len] == '\0' || p[len] == ','));
89}
90
91static void *
92xmalloc(size_t size)
93{
94	void *newptr;
95
96	if ((newptr = malloc(size)) == NULL)
97		err(1, "malloc()");
98	return (newptr);
99}
100
101static void *
102xrealloc(void *ptr, size_t size)
103{
104	void *newptr;
105
106	if ((newptr = realloc(ptr, size)) == NULL)
107		err(1, "realloc()");
108	return (newptr);
109}
110
111static char *
112xstrdup(const char *str)
113{
114	char *dupstr;
115
116	if ((dupstr = strdup(str)) == NULL)
117		err(1, "strdup()");
118	return (dupstr);
119}
120
121static struct xgroup	*grps;
122static size_t		 grpsz;
123static size_t		 ngrps;
124
125static void
126get_groups(void)
127{
128	struct group *grp;
129	size_t len;
130	int i;
131
132	setgrent();
133	for (;;) {
134		if (ngrps == grpsz) {
135			grpsz += grpsz ? grpsz : 128;
136			grps = xrealloc(grps, grpsz * sizeof *grps);
137		}
138		if ((grp = getgrent()) == NULL)
139			break;
140		grps[ngrps].gr_name = xstrdup(grp->gr_name);
141		grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd);
142		grps[ngrps].gr_gid = grp->gr_gid;
143		grps[ngrps].gr_mem = xstrdup("");
144		for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i)
145			len += strlen(grp->gr_mem[i]) + 1;
146		grps[ngrps].gr_mem = xmalloc(len);
147		for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i)
148			len += sprintf(grps[ngrps].gr_mem + len,
149			    i ? ",%s" : "%s", grp->gr_mem[i]);
150		grps[ngrps].gr_mem[len] = '\0';
151		ngrps++;
152	}
153	endgrent();
154}
155
156static struct xgroup *
157find_group_bygid(gid_t gid)
158{
159	unsigned int i;
160
161	for (i = 0; i < ngrps; ++i)
162		if (grps[i].gr_gid == gid)
163			return (&grps[i]);
164	return (NULL);
165}
166
167#if 0
168static struct xgroup *
169find_group_byname(const char *name)
170{
171	unsigned int i;
172
173	for (i = 0; i < ngrps; ++i)
174		if (strcmp(grps[i].gr_name, name) == 0)
175			return (&grps[i]);
176	return (NULL);
177}
178#endif
179
180static struct xpasswd	*pwds;
181static size_t		 pwdsz;
182static size_t		 npwds;
183
184static int
185pwd_cmp_byname(const void *ap, const void *bp)
186{
187	const struct passwd *a = ap;
188	const struct passwd *b = bp;
189
190	return (strcmp(a->pw_name, b->pw_name));
191}
192
193static int
194pwd_cmp_byuid(const void *ap, const void *bp)
195{
196	const struct passwd *a = ap;
197	const struct passwd *b = bp;
198
199	return (a->pw_uid - b->pw_uid);
200}
201
202static void
203get_users(void)
204{
205	struct passwd *pwd;
206
207	setpwent();
208	for (;;) {
209		if (npwds == pwdsz) {
210			pwdsz += pwdsz ? pwdsz : 128;
211			pwds = xrealloc(pwds, pwdsz * sizeof *pwds);
212		}
213		if ((pwd = getpwent()) == NULL)
214			break;
215		pwds[npwds].pw_name = xstrdup(pwd->pw_name);
216		pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd);
217		pwds[npwds].pw_uid = pwd->pw_uid;
218		pwds[npwds].pw_gid = pwd->pw_gid;
219		pwds[npwds].pw_change = pwd->pw_change;
220		pwds[npwds].pw_class = xstrdup(pwd->pw_class);
221		pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos);
222		pwds[npwds].pw_dir = xstrdup(pwd->pw_dir);
223		pwds[npwds].pw_shell = xstrdup(pwd->pw_shell);
224		pwds[npwds].pw_expire = pwd->pw_expire;
225		pwds[npwds].pw_selected = 0;
226		npwds++;
227	}
228	endpwent();
229}
230
231static void
232select_users(void)
233{
234	unsigned int i, j;
235	struct xgroup *grp;
236	struct xpasswd *pwd;
237
238	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) {
239		if (everything) {
240			pwd->pw_selected = 1;
241			continue;
242		}
243		if (d_flag)
244			if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) ||
245			    (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) {
246				pwd->pw_selected = 1;
247				continue;
248			}
249		if (g_args) {
250			for (j = 0, grp = grps; j < ngrps; ++j, ++grp) {
251				if (member(grp->gr_name, g_args) &&
252				    member(pwd->pw_name, grp->gr_mem)) {
253					pwd->pw_selected = 1;
254					break;
255				}
256			}
257			if (pwd->pw_selected)
258				continue;
259		}
260		if (l_args)
261			if (member(pwd->pw_name, l_args)) {
262				pwd->pw_selected = 1;
263				continue;
264			}
265		if (p_flag)
266			if (pwd->pw_passwd[0] == '\0') {
267				pwd->pw_selected = 1;
268				continue;
269			}
270		if (s_flag)
271			if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) {
272				pwd->pw_selected = 1;
273				continue;
274			}
275		if (u_flag)
276			if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) {
277				pwd->pw_selected = 1;
278				continue;
279			}
280	}
281}
282
283static void
284sort_users(void)
285{
286	if (t_flag)
287		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname);
288	else
289		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid);
290}
291
292static void
293display_user(struct xpasswd *pwd)
294{
295	struct xgroup *grp;
296	unsigned int i;
297	char cbuf[16], ebuf[16];
298	struct tm *tm;
299
300	grp = find_group_bygid(pwd->pw_gid);
301	printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n",
302	    pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "",
303	    (long)pwd->pw_gid, pwd->pw_gecos);
304	if (m_flag) {
305		for (i = 0, grp = grps; i < ngrps; ++i, ++grp) {
306			if (grp->gr_gid == pwd->pw_gid ||
307			    !member(pwd->pw_name, grp->gr_mem))
308				continue;
309			printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n",
310			    "", grp->gr_name, (long)grp->gr_gid);
311		}
312	}
313	if (x_flag) {
314		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir);
315		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell);
316	}
317	if (a_flag) {
318		tm = gmtime(&pwd->pw_change);
319		strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm);
320		tm = gmtime(&pwd->pw_expire);
321		strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm);
322		printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf);
323	}
324	if (o_flag)
325		printf("\n");
326}
327
328static void
329list_users(void)
330{
331	struct xpasswd *pwd;
332	unsigned int i;
333
334	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd)
335		if (pwd->pw_selected)
336			display_user(pwd);
337}
338
339static void
340usage(void)
341{
342	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
343	exit(1);
344}
345
346int
347main(int argc, char * const argv[])
348{
349	int o;
350
351	while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1)
352		switch (o) {
353		case 'a':
354			a_flag = 1;
355			break;
356		case 'd':
357			everything = 0;
358			d_flag = 1;
359			break;
360		case 'g':
361			everything = 0;
362			g_args = optarg;
363			break;
364		case 'l':
365			everything = 0;
366			l_args = optarg;
367			break;
368		case 'm':
369			m_flag = 1;
370			break;
371		case 'o':
372			o_flag = 1;
373			break;
374		case 'p':
375			everything = 0;
376			p_flag = 1;
377			break;
378		case 's':
379			everything = 0;
380			s_flag = 1;
381			break;
382		case 't':
383			t_flag = 1;
384			break;
385		case 'u':
386			everything = 0;
387			u_flag = 1;
388			break;
389		case 'x':
390			x_flag = 1;
391			break;
392		default:
393			usage();
394		}
395
396	argc -= optind;
397	argv += optind;
398
399	if (argc > 0)
400		usage();
401
402	get_groups();
403	get_users();
404	select_users();
405	sort_users();
406	list_users();
407	exit(0);
408}
409