133965Sjdp/*-
278828Sobrien * Copyright (c) 2004 Dag-Erling Co��dan Sm��rgrav
3218822Sdim * All rights reserved.
438889Sjdp *
533965Sjdp * Redistribution and use in source and binary forms, with or without
633965Sjdp * modification, are permitted provided that the following conditions
7218822Sdim * are met:
833965Sjdp * 1. Redistributions of source code must retain the above copyright
9218822Sdim *    notice, this list of conditions and the following disclaimer
10218822Sdim *    in this position and unchanged.
11218822Sdim * 2. Redistributions in binary form must reproduce the above copyright
12218822Sdim *    notice, this list of conditions and the following disclaimer in the
1333965Sjdp *    documentation and/or other materials provided with the distribution.
14218822Sdim * 3. The name of the author may not be used to endorse or promote products
15218822Sdim *    derived from this software without specific prior written permission.
16218822Sdim *
17218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1833965Sjdp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19218822Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20218822Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21218822Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2233965Sjdp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23218822Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2433965Sjdp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2533965Sjdp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26218822Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2777298Sobrien *
2833965Sjdp * $FreeBSD$
2933965Sjdp */
3033965Sjdp
3133965Sjdp#include <sys/cdefs.h>
3233965Sjdp__FBSDID("$FreeBSD$");
3333965Sjdp
3433965Sjdp#include <err.h>
3533965Sjdp#include <grp.h>
3633965Sjdp#include <pwd.h>
3733965Sjdp#include <stdio.h>
3833965Sjdp#include <stdlib.h>
3933965Sjdp#include <string.h>
4033965Sjdp#include <time.h>
4133965Sjdp#include <unistd.h>
4233965Sjdp
4333965Sjdpstruct xpasswd {
4433965Sjdp	char		*pw_name;
4577298Sobrien	char		*pw_passwd;
46218822Sdim	uid_t		 pw_uid;
4733965Sjdp	gid_t		 pw_gid;
4877298Sobrien	time_t		 pw_change;
49218822Sdim	char		*pw_class;
5033965Sjdp	char		*pw_gecos;
5133965Sjdp	char		*pw_dir;
5233965Sjdp	char		*pw_shell;
5333965Sjdp	time_t		 pw_expire;
5433965Sjdp	int		 pw_selected;
5533965Sjdp};
5633965Sjdp
5733965Sjdpstruct xgroup {
5833965Sjdp	char		*gr_name;
5933965Sjdp	char		*gr_passwd;
6033965Sjdp	gid_t		 gr_gid;
6133965Sjdp	char		*gr_mem;
6233965Sjdp};
6333965Sjdp
6477298Sobrienstatic int		 everything = 1;
6533965Sjdpstatic int		 a_flag;
6633965Sjdpstatic int		 d_flag;
6733965Sjdpstatic const char	*g_args;
6833965Sjdpstatic const char	*l_args;
6933965Sjdpstatic int		 m_flag;
7033965Sjdpstatic int		 o_flag;
7133965Sjdpstatic int		 p_flag;
7233965Sjdpstatic int		 s_flag;
7333965Sjdpstatic int		 t_flag;
74218822Sdimstatic int		 u_flag;
7533965Sjdpstatic int		 x_flag;
7633965Sjdp
77218822Sdimstatic int
7833965Sjdpmember(const char *elem, const char *list)
7933965Sjdp{
8033965Sjdp	char *p;
8133965Sjdp	int len;
8233965Sjdp
8333965Sjdp	p = strstr(list, elem);
8433965Sjdp	len = strlen(elem);
8533965Sjdp
8633965Sjdp	return (p != NULL &&
8733965Sjdp	    (p == list || p[-1] == ',') &&
8833965Sjdp	    (p[len] == '\0' || p[len] == ','));
89218822Sdim}
9033965Sjdp
9133965Sjdpstatic void *
9233965Sjdpxmalloc(size_t size)
9333965Sjdp{
9433965Sjdp	void *newptr;
9533965Sjdp
9633965Sjdp	if ((newptr = malloc(size)) == NULL)
9733965Sjdp		err(1, "malloc()");
9833965Sjdp	return (newptr);
9933965Sjdp}
100218822Sdim
10133965Sjdpstatic void *
102218822Sdimxrealloc(void *ptr, size_t size)
10377298Sobrien{
104218822Sdim	void *newptr;
10533965Sjdp
10633965Sjdp	if ((newptr = realloc(ptr, size)) == NULL)
10733965Sjdp		err(1, "realloc()");
10889857Sobrien	return (newptr);
10933965Sjdp}
11033965Sjdp
11177298Sobrienstatic char *
11233965Sjdpxstrdup(const char *str)
11389857Sobrien{
11433965Sjdp	char *dupstr;
115218822Sdim
116218822Sdim	if ((dupstr = strdup(str)) == NULL)
11733965Sjdp		err(1, "strdup()");
11833965Sjdp	return (dupstr);
11933965Sjdp}
12033965Sjdp
121218822Sdimstatic struct xgroup	*grps;
12233965Sjdpstatic size_t		 grpsz;
12394536Sobrienstatic size_t		 ngrps;
12433965Sjdp
12533965Sjdpstatic void
12677298Sobrienget_groups(void)
12777298Sobrien{
12877298Sobrien	struct group *grp;
12994536Sobrien	size_t len;
13077298Sobrien	int i;
13177298Sobrien
13277298Sobrien	setgrent();
13377298Sobrien	for (;;) {
13477298Sobrien		if (ngrps == grpsz) {
13577298Sobrien			grpsz += grpsz ? grpsz : 128;
13677298Sobrien			grps = xrealloc(grps, grpsz * sizeof *grps);
137130561Sobrien		}
13877298Sobrien		if ((grp = getgrent()) == NULL)
13933965Sjdp			break;
14033965Sjdp		grps[ngrps].gr_name = xstrdup(grp->gr_name);
14133965Sjdp		grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd);
14233965Sjdp		grps[ngrps].gr_gid = grp->gr_gid;
14333965Sjdp		grps[ngrps].gr_mem = xstrdup("");
14433965Sjdp		for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i)
14533965Sjdp			len += strlen(grp->gr_mem[i]) + 1;
14633965Sjdp		grps[ngrps].gr_mem = xmalloc(len);
14733965Sjdp		for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i)
14833965Sjdp			len += sprintf(grps[ngrps].gr_mem + len,
14933965Sjdp			    i ? ",%s" : "%s", grp->gr_mem[i]);
15033965Sjdp		grps[ngrps].gr_mem[len] = '\0';
15133965Sjdp		ngrps++;
15233965Sjdp	}
15333965Sjdp	endgrent();
15433965Sjdp}
15533965Sjdp
15633965Sjdpstatic struct xgroup *
15733965Sjdpfind_group_bygid(gid_t gid)
15833965Sjdp{
15933965Sjdp	unsigned int i;
16033965Sjdp
16133965Sjdp	for (i = 0; i < ngrps; ++i)
16233965Sjdp		if (grps[i].gr_gid == gid)
16333965Sjdp			return (&grps[i]);
16433965Sjdp	return (NULL);
16533965Sjdp}
16633965Sjdp
16733965Sjdp#if 0
16833965Sjdpstatic struct xgroup *
16933965Sjdpfind_group_byname(const char *name)
17033965Sjdp{
17133965Sjdp	unsigned int i;
17233965Sjdp
17333965Sjdp	for (i = 0; i < ngrps; ++i)
17433965Sjdp		if (strcmp(grps[i].gr_name, name) == 0)
17533965Sjdp			return (&grps[i]);
176218822Sdim	return (NULL);
17733965Sjdp}
178218822Sdim#endif
179218822Sdim
180218822Sdimstatic struct xpasswd	*pwds;
181218822Sdimstatic size_t		 pwdsz;
182218822Sdimstatic size_t		 npwds;
183218822Sdim
184218822Sdimstatic int
185218822Sdimpwd_cmp_byname(const void *ap, const void *bp)
186218822Sdim{
187218822Sdim	const struct passwd *a = ap;
188218822Sdim	const struct passwd *b = bp;
189218822Sdim
19033965Sjdp	return (strcmp(a->pw_name, b->pw_name));
19133965Sjdp}
192130561Sobrien
193218822Sdimstatic int
19433965Sjdppwd_cmp_byuid(const void *ap, const void *bp)
195104834Sobrien{
196104834Sobrien	const struct passwd *a = ap;
19733965Sjdp	const struct passwd *b = bp;
19833965Sjdp
199218822Sdim	return (a->pw_uid - b->pw_uid);
200104834Sobrien}
201130561Sobrien
202130561Sobrienstatic void
203104834Sobrienget_users(void)
204104834Sobrien{
205104834Sobrien	struct passwd *pwd;
206104834Sobrien
207104834Sobrien	setpwent();
208104834Sobrien	for (;;) {
209104834Sobrien		if (npwds == pwdsz) {
21033965Sjdp			pwdsz += pwdsz ? pwdsz : 128;
211130561Sobrien			pwds = xrealloc(pwds, pwdsz * sizeof *pwds);
21233965Sjdp		}
21333965Sjdp		if ((pwd = getpwent()) == NULL)
21433965Sjdp			break;
21533965Sjdp		pwds[npwds].pw_name = xstrdup(pwd->pw_name);
21633965Sjdp		pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd);
21733965Sjdp		pwds[npwds].pw_uid = pwd->pw_uid;
218218822Sdim		pwds[npwds].pw_gid = pwd->pw_gid;
21933965Sjdp		pwds[npwds].pw_change = pwd->pw_change;
22033965Sjdp		pwds[npwds].pw_class = xstrdup(pwd->pw_class);
22133965Sjdp		pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos);
22289857Sobrien		pwds[npwds].pw_dir = xstrdup(pwd->pw_dir);
22333965Sjdp		pwds[npwds].pw_shell = xstrdup(pwd->pw_shell);
22433965Sjdp		pwds[npwds].pw_expire = pwd->pw_expire;
225130561Sobrien		pwds[npwds].pw_selected = 0;
22633965Sjdp		npwds++;
22733965Sjdp	}
22833965Sjdp	endpwent();
22933965Sjdp}
23033965Sjdp
23133965Sjdpstatic void
23233965Sjdpselect_users(void)
23333965Sjdp{
23433965Sjdp	unsigned int i, j;
23533965Sjdp	struct xgroup *grp;
23633965Sjdp	struct xpasswd *pwd;
237218822Sdim
238218822Sdim	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) {
239218822Sdim		if (everything) {
240218822Sdim			pwd->pw_selected = 1;
24133965Sjdp			continue;
24233965Sjdp		}
24333965Sjdp		if (d_flag)
24433965Sjdp			if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) ||
24533965Sjdp			    (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) {
24633965Sjdp				pwd->pw_selected = 1;
24733965Sjdp				continue;
24833965Sjdp			}
24933965Sjdp		if (g_args) {
25033965Sjdp			for (j = 0, grp = grps; j < ngrps; ++j, ++grp) {
25189857Sobrien				if (member(grp->gr_name, g_args) &&
25233965Sjdp				    member(pwd->pw_name, grp->gr_mem)) {
25333965Sjdp					pwd->pw_selected = 1;
25433965Sjdp					break;
25533965Sjdp				}
25633965Sjdp			}
25733965Sjdp			if (pwd->pw_selected)
25833965Sjdp				continue;
259218822Sdim		}
260218822Sdim		if (l_args)
26133965Sjdp			if (member(pwd->pw_name, l_args)) {
26233965Sjdp				pwd->pw_selected = 1;
26333965Sjdp				continue;
26433965Sjdp			}
26533965Sjdp		if (p_flag)
26633965Sjdp			if (pwd->pw_passwd[0] == '\0') {
267130561Sobrien				pwd->pw_selected = 1;
268218822Sdim				continue;
26933965Sjdp			}
27033965Sjdp		if (s_flag)
27133965Sjdp			if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) {
272218822Sdim				pwd->pw_selected = 1;
27333965Sjdp				continue;
274130561Sobrien			}
27533965Sjdp		if (u_flag)
27633965Sjdp			if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) {
27733965Sjdp				pwd->pw_selected = 1;
27833965Sjdp				continue;
27933965Sjdp			}
28033965Sjdp	}
28133965Sjdp}
28233965Sjdp
28333965Sjdpstatic void
28433965Sjdpsort_users(void)
28533965Sjdp{
28633965Sjdp	if (t_flag)
28733965Sjdp		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname);
288130561Sobrien	else
28933965Sjdp		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid);
29033965Sjdp}
29133965Sjdp
29233965Sjdpstatic void
29333965Sjdpdisplay_user(struct xpasswd *pwd)
294130561Sobrien{
295218822Sdim	struct xgroup *grp;
29633965Sjdp	unsigned int i;
29733965Sjdp	char cbuf[16], ebuf[16];
29833965Sjdp	struct tm *tm;
299130561Sobrien
30033965Sjdp	grp = find_group_bygid(pwd->pw_gid);
30133965Sjdp	printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n",
30233965Sjdp	    pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "",
30333965Sjdp	    (long)pwd->pw_gid, pwd->pw_gecos);
30433965Sjdp	if (m_flag) {
30533965Sjdp		for (i = 0, grp = grps; i < ngrps; ++i, ++grp) {
30633965Sjdp			if (grp->gr_gid == pwd->pw_gid ||
30733965Sjdp			    !member(pwd->pw_name, grp->gr_mem))
30833965Sjdp				continue;
30933965Sjdp			printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n",
31033965Sjdp			    "", grp->gr_name, (long)grp->gr_gid);
311130561Sobrien		}
31233965Sjdp	}
31333965Sjdp	if (x_flag) {
31433965Sjdp		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir);
31533965Sjdp		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell);
31633965Sjdp	}
31733965Sjdp	if (a_flag) {
31833965Sjdp		tm = gmtime(&pwd->pw_change);
31933965Sjdp		strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm);
32033965Sjdp		tm = gmtime(&pwd->pw_expire);
32133965Sjdp		strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm);
32233965Sjdp		printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf);
32333965Sjdp	}
32433965Sjdp	if (o_flag)
32533965Sjdp		printf("\n");
32633965Sjdp}
32733965Sjdp
32833965Sjdpstatic void
32933965Sjdplist_users(void)
33033965Sjdp{
33133965Sjdp	struct xpasswd *pwd;
33233965Sjdp	unsigned int i;
33333965Sjdp
33433965Sjdp	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd)
33533965Sjdp		if (pwd->pw_selected)
33633965Sjdp			display_user(pwd);
33733965Sjdp}
33833965Sjdp
33933965Sjdpstatic void
34033965Sjdpusage(void)
34133965Sjdp{
34233965Sjdp	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
34333965Sjdp	exit(1);
34433965Sjdp}
34589857Sobrien
34633965Sjdpint
34733965Sjdpmain(int argc, char * const argv[])
34833965Sjdp{
34933965Sjdp	int o;
35033965Sjdp
35133965Sjdp	while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1)
35233965Sjdp		switch (o) {
35333965Sjdp		case 'a':
35460484Sobrien			a_flag = 1;
35533965Sjdp			break;
35633965Sjdp		case 'd':
35733965Sjdp			everything = 0;
35833965Sjdp			d_flag = 1;
35933965Sjdp			break;
36033965Sjdp		case 'g':
36133965Sjdp			everything = 0;
36233965Sjdp			g_args = optarg;
36333965Sjdp			break;
364218822Sdim		case 'l':
36533965Sjdp			everything = 0;
36633965Sjdp			l_args = optarg;
36733965Sjdp			break;
36833965Sjdp		case 'm':
36933965Sjdp			m_flag = 1;
37033965Sjdp			break;
37133965Sjdp		case 'o':
37289857Sobrien			o_flag = 1;
37333965Sjdp			break;
37489857Sobrien		case 'p':
37533965Sjdp			everything = 0;
37633965Sjdp			p_flag = 1;
37733965Sjdp			break;
37833965Sjdp		case 's':
379218822Sdim			everything = 0;
38033965Sjdp			s_flag = 1;
38133965Sjdp			break;
38233965Sjdp		case 't':
38333965Sjdp			t_flag = 1;
38433965Sjdp			break;
38533965Sjdp		case 'u':
38633965Sjdp			everything = 0;
38733965Sjdp			u_flag = 1;
38833965Sjdp			break;
38933965Sjdp		case 'x':
39033965Sjdp			x_flag = 1;
39133965Sjdp			break;
39233965Sjdp		default:
39333965Sjdp			usage();
39433965Sjdp		}
39533965Sjdp
39689857Sobrien	argc -= optind;
39733965Sjdp	argv += optind;
39833965Sjdp
39933965Sjdp	if (argc > 0)
40033965Sjdp		usage();
40133965Sjdp
40233965Sjdp	get_groups();
40333965Sjdp	get_users();
40433965Sjdp	select_users();
40533965Sjdp	sort_users();
40633965Sjdp	list_users();
40733965Sjdp	exit(0);
40833965Sjdp}
40933965Sjdp