xref: /freebsd/usr.bin/logins/logins.c (revision c697fb7f)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2004 Dag-Erling Coïdan 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  * $FreeBSD$
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <err.h>
37 #include <grp.h>
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 struct xpasswd {
46 	char		*pw_name;
47 	char		*pw_passwd;
48 	uid_t		 pw_uid;
49 	gid_t		 pw_gid;
50 	time_t		 pw_change;
51 	char		*pw_class;
52 	char		*pw_gecos;
53 	char		*pw_dir;
54 	char		*pw_shell;
55 	time_t		 pw_expire;
56 	int		 pw_selected;
57 };
58 
59 struct xgroup {
60 	char		*gr_name;
61 	char		*gr_passwd;
62 	gid_t		 gr_gid;
63 	char		*gr_mem;
64 };
65 
66 static int		 everything = 1;
67 static int		 a_flag;
68 static int		 d_flag;
69 static const char	*g_args;
70 static const char	*l_args;
71 static int		 m_flag;
72 static int		 o_flag;
73 static int		 p_flag;
74 static int		 s_flag;
75 static int		 t_flag;
76 static int		 u_flag;
77 static int		 x_flag;
78 
79 static int
80 member(const char *elem, const char *list)
81 {
82 	char *p;
83 	int len;
84 
85 	p = strstr(list, elem);
86 	len = strlen(elem);
87 
88 	return (p != NULL &&
89 	    (p == list || p[-1] == ',') &&
90 	    (p[len] == '\0' || p[len] == ','));
91 }
92 
93 static void *
94 xmalloc(size_t size)
95 {
96 	void *newptr;
97 
98 	if ((newptr = malloc(size)) == NULL)
99 		err(1, "malloc()");
100 	return (newptr);
101 }
102 
103 static void *
104 xrealloc(void *ptr, size_t size)
105 {
106 	void *newptr;
107 
108 	if ((newptr = realloc(ptr, size)) == NULL)
109 		err(1, "realloc()");
110 	return (newptr);
111 }
112 
113 static char *
114 xstrdup(const char *str)
115 {
116 	char *dupstr;
117 
118 	if ((dupstr = strdup(str)) == NULL)
119 		err(1, "strdup()");
120 	return (dupstr);
121 }
122 
123 static struct xgroup	*grps;
124 static size_t		 grpsz;
125 static size_t		 ngrps;
126 
127 static void
128 get_groups(void)
129 {
130 	struct group *grp;
131 	size_t len;
132 	int i;
133 
134 	setgrent();
135 	for (;;) {
136 		if (ngrps == grpsz) {
137 			grpsz += grpsz ? grpsz : 128;
138 			grps = xrealloc(grps, grpsz * sizeof *grps);
139 		}
140 		if ((grp = getgrent()) == NULL)
141 			break;
142 		grps[ngrps].gr_name = xstrdup(grp->gr_name);
143 		grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd);
144 		grps[ngrps].gr_gid = grp->gr_gid;
145 		grps[ngrps].gr_mem = xstrdup("");
146 		for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i)
147 			len += strlen(grp->gr_mem[i]) + 1;
148 		grps[ngrps].gr_mem = xmalloc(len);
149 		for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i)
150 			len += sprintf(grps[ngrps].gr_mem + len,
151 			    i ? ",%s" : "%s", grp->gr_mem[i]);
152 		grps[ngrps].gr_mem[len] = '\0';
153 		ngrps++;
154 	}
155 	endgrent();
156 }
157 
158 static struct xgroup *
159 find_group_bygid(gid_t gid)
160 {
161 	unsigned int i;
162 
163 	for (i = 0; i < ngrps; ++i)
164 		if (grps[i].gr_gid == gid)
165 			return (&grps[i]);
166 	return (NULL);
167 }
168 
169 #if 0
170 static struct xgroup *
171 find_group_byname(const char *name)
172 {
173 	unsigned int i;
174 
175 	for (i = 0; i < ngrps; ++i)
176 		if (strcmp(grps[i].gr_name, name) == 0)
177 			return (&grps[i]);
178 	return (NULL);
179 }
180 #endif
181 
182 static struct xpasswd	*pwds;
183 static size_t		 pwdsz;
184 static size_t		 npwds;
185 
186 static int
187 pwd_cmp_byname(const void *ap, const void *bp)
188 {
189 	const struct passwd *a = ap;
190 	const struct passwd *b = bp;
191 
192 	return (strcmp(a->pw_name, b->pw_name));
193 }
194 
195 static int
196 pwd_cmp_byuid(const void *ap, const void *bp)
197 {
198 	const struct passwd *a = ap;
199 	const struct passwd *b = bp;
200 
201 	return (a->pw_uid - b->pw_uid);
202 }
203 
204 static void
205 get_users(void)
206 {
207 	struct passwd *pwd;
208 
209 	setpwent();
210 	for (;;) {
211 		if (npwds == pwdsz) {
212 			pwdsz += pwdsz ? pwdsz : 128;
213 			pwds = xrealloc(pwds, pwdsz * sizeof *pwds);
214 		}
215 		if ((pwd = getpwent()) == NULL)
216 			break;
217 		pwds[npwds].pw_name = xstrdup(pwd->pw_name);
218 		pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd);
219 		pwds[npwds].pw_uid = pwd->pw_uid;
220 		pwds[npwds].pw_gid = pwd->pw_gid;
221 		pwds[npwds].pw_change = pwd->pw_change;
222 		pwds[npwds].pw_class = xstrdup(pwd->pw_class);
223 		pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos);
224 		pwds[npwds].pw_dir = xstrdup(pwd->pw_dir);
225 		pwds[npwds].pw_shell = xstrdup(pwd->pw_shell);
226 		pwds[npwds].pw_expire = pwd->pw_expire;
227 		pwds[npwds].pw_selected = 0;
228 		npwds++;
229 	}
230 	endpwent();
231 }
232 
233 static void
234 select_users(void)
235 {
236 	unsigned int i, j;
237 	struct xgroup *grp;
238 	struct xpasswd *pwd;
239 
240 	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) {
241 		if (everything) {
242 			pwd->pw_selected = 1;
243 			continue;
244 		}
245 		if (d_flag)
246 			if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) ||
247 			    (i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) {
248 				pwd->pw_selected = 1;
249 				continue;
250 			}
251 		if (g_args) {
252 			for (j = 0, grp = grps; j < ngrps; ++j, ++grp) {
253 				if (member(grp->gr_name, g_args) &&
254 				    member(pwd->pw_name, grp->gr_mem)) {
255 					pwd->pw_selected = 1;
256 					break;
257 				}
258 			}
259 			if (pwd->pw_selected)
260 				continue;
261 		}
262 		if (l_args)
263 			if (member(pwd->pw_name, l_args)) {
264 				pwd->pw_selected = 1;
265 				continue;
266 			}
267 		if (p_flag)
268 			if (pwd->pw_passwd[0] == '\0') {
269 				pwd->pw_selected = 1;
270 				continue;
271 			}
272 		if (s_flag)
273 			if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) {
274 				pwd->pw_selected = 1;
275 				continue;
276 			}
277 		if (u_flag)
278 			if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) {
279 				pwd->pw_selected = 1;
280 				continue;
281 			}
282 	}
283 }
284 
285 static void
286 sort_users(void)
287 {
288 	if (t_flag)
289 		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname);
290 	else
291 		mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid);
292 }
293 
294 static void
295 display_user(struct xpasswd *pwd)
296 {
297 	struct xgroup *grp;
298 	unsigned int i;
299 	char cbuf[16], ebuf[16];
300 	struct tm *tm;
301 
302 	grp = find_group_bygid(pwd->pw_gid);
303 	printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n",
304 	    pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "",
305 	    (long)pwd->pw_gid, pwd->pw_gecos);
306 	if (m_flag) {
307 		for (i = 0, grp = grps; i < ngrps; ++i, ++grp) {
308 			if (grp->gr_gid == pwd->pw_gid ||
309 			    !member(pwd->pw_name, grp->gr_mem))
310 				continue;
311 			printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n",
312 			    "", grp->gr_name, (long)grp->gr_gid);
313 		}
314 	}
315 	if (x_flag) {
316 		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir);
317 		printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell);
318 	}
319 	if (a_flag) {
320 		tm = gmtime(&pwd->pw_change);
321 		strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm);
322 		tm = gmtime(&pwd->pw_expire);
323 		strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm);
324 		printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf);
325 	}
326 	if (o_flag)
327 		printf("\n");
328 }
329 
330 static void
331 list_users(void)
332 {
333 	struct xpasswd *pwd;
334 	unsigned int i;
335 
336 	for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd)
337 		if (pwd->pw_selected)
338 			display_user(pwd);
339 }
340 
341 static void
342 usage(void)
343 {
344 	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
345 	exit(1);
346 }
347 
348 int
349 main(int argc, char * const argv[])
350 {
351 	int o;
352 
353 	while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1)
354 		switch (o) {
355 		case 'a':
356 			a_flag = 1;
357 			break;
358 		case 'd':
359 			everything = 0;
360 			d_flag = 1;
361 			break;
362 		case 'g':
363 			everything = 0;
364 			g_args = optarg;
365 			break;
366 		case 'l':
367 			everything = 0;
368 			l_args = optarg;
369 			break;
370 		case 'm':
371 			m_flag = 1;
372 			break;
373 		case 'o':
374 			o_flag = 1;
375 			break;
376 		case 'p':
377 			everything = 0;
378 			p_flag = 1;
379 			break;
380 		case 's':
381 			everything = 0;
382 			s_flag = 1;
383 			break;
384 		case 't':
385 			t_flag = 1;
386 			break;
387 		case 'u':
388 			everything = 0;
389 			u_flag = 1;
390 			break;
391 		case 'x':
392 			x_flag = 1;
393 			break;
394 		default:
395 			usage();
396 		}
397 
398 	argc -= optind;
399 	argv += optind;
400 
401 	if (argc > 0)
402 		usage();
403 
404 	get_groups();
405 	get_users();
406 	select_users();
407 	sort_users();
408 	list_users();
409 	exit(0);
410 }
411