xref: /freebsd/usr.bin/logins/logins.c (revision 276da39af92f48350aa01091a2b8b3e735217eea)
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 
43 struct 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 
57 struct xgroup {
58 	char		*gr_name;
59 	char		*gr_passwd;
60 	gid_t		 gr_gid;
61 	char		*gr_mem;
62 };
63 
64 static int		 everything = 1;
65 static int		 a_flag;
66 static int		 d_flag;
67 static const char	*g_args;
68 static const char	*l_args;
69 static int		 m_flag;
70 static int		 o_flag;
71 static int		 p_flag;
72 static int		 s_flag;
73 static int		 t_flag;
74 static int		 u_flag;
75 static int		 x_flag;
76 
77 static int
78 member(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 
91 static void *
92 xmalloc(size_t size)
93 {
94 	void *newptr;
95 
96 	if ((newptr = malloc(size)) == NULL)
97 		err(1, "malloc()");
98 	return (newptr);
99 }
100 
101 static void *
102 xrealloc(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 
111 static char *
112 xstrdup(const char *str)
113 {
114 	char *dupstr;
115 
116 	if ((dupstr = strdup(str)) == NULL)
117 		err(1, "strdup()");
118 	return (dupstr);
119 }
120 
121 static struct xgroup	*grps;
122 static size_t		 grpsz;
123 static size_t		 ngrps;
124 
125 static void
126 get_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 
156 static struct xgroup *
157 find_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
168 static struct xgroup *
169 find_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 
180 static struct xpasswd	*pwds;
181 static size_t		 pwdsz;
182 static size_t		 npwds;
183 
184 static int
185 pwd_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 
193 static int
194 pwd_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 
202 static void
203 get_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 
231 static void
232 select_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 
283 static void
284 sort_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 
292 static void
293 display_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 
328 static void
329 list_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 
339 static void
340 usage(void)
341 {
342 	fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
343 	exit(1);
344 }
345 
346 int
347 main(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