xref: /freebsd/usr.bin/logins/logins.c (revision e738085b94631f90e21a49852538ac95974baf44)
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 
41 struct 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 
55 struct xgroup {
56 	char		*gr_name;
57 	char		*gr_passwd;
58 	gid_t		 gr_gid;
59 	char		*gr_mem;
60 };
61 
62 static int		 everything = 1;
63 static int		 a_flag;
64 static int		 d_flag;
65 static const char	*g_args;
66 static const char	*l_args;
67 static int		 m_flag;
68 static int		 o_flag;
69 static int		 p_flag;
70 static int		 s_flag;
71 static int		 t_flag;
72 static int		 u_flag;
73 static int		 x_flag;
74 
75 static int
member(const char * elem,const char * list)76 member(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 
89 static void *
xmalloc(size_t size)90 xmalloc(size_t size)
91 {
92 	void *newptr;
93 
94 	if ((newptr = malloc(size)) == NULL)
95 		err(1, "malloc()");
96 	return (newptr);
97 }
98 
99 static void *
xrealloc(void * ptr,size_t size)100 xrealloc(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 
109 static char *
xstrdup(const char * str)110 xstrdup(const char *str)
111 {
112 	char *dupstr;
113 
114 	if ((dupstr = strdup(str)) == NULL)
115 		err(1, "strdup()");
116 	return (dupstr);
117 }
118 
119 static struct xgroup	*grps;
120 static size_t		 grpsz;
121 static size_t		 ngrps;
122 
123 static void
get_groups(void)124 get_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 
154 static struct xgroup *
find_group_bygid(gid_t gid)155 find_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
166 static struct xgroup *
167 find_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 
178 static struct xpasswd	*pwds;
179 static size_t		 pwdsz;
180 static size_t		 npwds;
181 
182 static int
pwd_cmp_byname(const void * ap,const void * bp)183 pwd_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 
191 static int
pwd_cmp_byuid(const void * ap,const void * bp)192 pwd_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 
200 static void
get_users(void)201 get_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 
229 static void
select_users(void)230 select_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 
281 static void
sort_users(void)282 sort_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 
290 static void
display_user(struct xpasswd * pwd)291 display_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 
326 static void
list_users(void)327 list_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 
337 static void
usage(void)338 usage(void)
339 {
340 	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
341 	exit(1);
342 }
343 
344 int
main(int argc,char * const argv[])345 main(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