/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2004 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <err.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
struct xpasswd {
char *pw_name;
char *pw_passwd;
uid_t pw_uid;
gid_t pw_gid;
time_t pw_change;
char *pw_class;
char *pw_gecos;
char *pw_dir;
char *pw_shell;
time_t pw_expire;
int pw_selected;
};
struct xgroup {
char *gr_name;
char *gr_passwd;
gid_t gr_gid;
char *gr_mem;
};
static int everything = 1;
static int a_flag;
static int d_flag;
static const char *g_args;
static const char *l_args;
static int m_flag;
static int o_flag;
static int p_flag;
static int s_flag;
static int t_flag;
static int u_flag;
static int x_flag;
static int
member(const char *elem, const char *list)
{
char *p;
int len;
p = strstr(list, elem);
len = strlen(elem);
return (p != NULL &&
(p == list || p[-1] == ',') &&
(p[len] == '\0' || p[len] == ','));
}
static void *
xmalloc(size_t size)
{
void *newptr;
if ((newptr = malloc(size)) == NULL)
err(1, "malloc()");
return (newptr);
}
static void *
xrealloc(void *ptr, size_t size)
{
void *newptr;
if ((newptr = realloc(ptr, size)) == NULL)
err(1, "realloc()");
return (newptr);
}
static char *
xstrdup(const char *str)
{
char *dupstr;
if ((dupstr = strdup(str)) == NULL)
err(1, "strdup()");
return (dupstr);
}
static struct xgroup *grps;
static size_t grpsz;
static size_t ngrps;
static void
get_groups(void)
{
struct group *grp;
size_t len;
int i;
setgrent();
for (;;) {
if (ngrps == grpsz) {
grpsz += grpsz ? grpsz : 128;
grps = xrealloc(grps, grpsz * sizeof *grps);
}
if ((grp = getgrent()) == NULL)
break;
grps[ngrps].gr_name = xstrdup(grp->gr_name);
grps[ngrps].gr_passwd = xstrdup(grp->gr_passwd);
grps[ngrps].gr_gid = grp->gr_gid;
grps[ngrps].gr_mem = xstrdup("");
for (i = 0, len = 1; grp->gr_mem[i] != NULL; ++i)
len += strlen(grp->gr_mem[i]) + 1;
grps[ngrps].gr_mem = xmalloc(len);
for (i = 0, len = 0; grp->gr_mem[i] != NULL; ++i)
len += sprintf(grps[ngrps].gr_mem + len,
i ? ",%s" : "%s", grp->gr_mem[i]);
grps[ngrps].gr_mem[len] = '\0';
ngrps++;
}
endgrent();
}
static struct xgroup *
find_group_bygid(gid_t gid)
{
unsigned int i;
for (i = 0; i < ngrps; ++i)
if (grps[i].gr_gid == gid)
return (&grps[i]);
return (NULL);
}
#if 0
static struct xgroup *
find_group_byname(const char *name)
{
unsigned int i;
for (i = 0; i < ngrps; ++i)
if (strcmp(grps[i].gr_name, name) == 0)
return (&grps[i]);
return (NULL);
}
#endif
static struct xpasswd *pwds;
static size_t pwdsz;
static size_t npwds;
static int
pwd_cmp_byname(const void *ap, const void *bp)
{
const struct passwd *a = ap;
const struct passwd *b = bp;
return (strcmp(a->pw_name, b->pw_name));
}
static int
pwd_cmp_byuid(const void *ap, const void *bp)
{
const struct passwd *a = ap;
const struct passwd *b = bp;
return (a->pw_uid - b->pw_uid);
}
static void
get_users(void)
{
struct passwd *pwd;
setpwent();
for (;;) {
if (npwds == pwdsz) {
pwdsz += pwdsz ? pwdsz : 128;
pwds = xrealloc(pwds, pwdsz * sizeof *pwds);
}
if ((pwd = getpwent()) == NULL)
break;
pwds[npwds].pw_name = xstrdup(pwd->pw_name);
pwds[npwds].pw_passwd = xstrdup(pwd->pw_passwd);
pwds[npwds].pw_uid = pwd->pw_uid;
pwds[npwds].pw_gid = pwd->pw_gid;
pwds[npwds].pw_change = pwd->pw_change;
pwds[npwds].pw_class = xstrdup(pwd->pw_class);
pwds[npwds].pw_gecos = xstrdup(pwd->pw_gecos);
pwds[npwds].pw_dir = xstrdup(pwd->pw_dir);
pwds[npwds].pw_shell = xstrdup(pwd->pw_shell);
pwds[npwds].pw_expire = pwd->pw_expire;
pwds[npwds].pw_selected = 0;
npwds++;
}
endpwent();
}
static void
select_users(void)
{
unsigned int i, j;
struct xgroup *grp;
struct xpasswd *pwd;
for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd) {
if (everything) {
pwd->pw_selected = 1;
continue;
}
if (d_flag)
if ((i > 0 && pwd->pw_uid == pwd[-1].pw_uid) ||
(i < npwds - 1 && pwd->pw_uid == pwd[1].pw_uid)) {
pwd->pw_selected = 1;
continue;
}
if (g_args) {
for (j = 0, grp = grps; j < ngrps; ++j, ++grp) {
if (member(grp->gr_name, g_args) &&
member(pwd->pw_name, grp->gr_mem)) {
pwd->pw_selected = 1;
break;
}
}
if (pwd->pw_selected)
continue;
}
if (l_args)
if (member(pwd->pw_name, l_args)) {
pwd->pw_selected = 1;
continue;
}
if (p_flag)
if (pwd->pw_passwd[0] == '\0') {
pwd->pw_selected = 1;
continue;
}
if (s_flag)
if (pwd->pw_uid < 1000 || pwd->pw_uid == 65534) {
pwd->pw_selected = 1;
continue;
}
if (u_flag)
if (pwd->pw_uid >= 1000 && pwd->pw_uid != 65534) {
pwd->pw_selected = 1;
continue;
}
}
}
static void
sort_users(void)
{
if (t_flag)
mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byname);
else
mergesort(pwds, npwds, sizeof *pwds, pwd_cmp_byuid);
}
static void
display_user(struct xpasswd *pwd)
{
struct xgroup *grp;
unsigned int i;
char cbuf[16], ebuf[16];
struct tm *tm;
grp = find_group_bygid(pwd->pw_gid);
printf(o_flag ? "%s:%ld:%s:%ld:%s" : "%-15s %-7ld %-15s %-7ld %s\n",
pwd->pw_name, (long)pwd->pw_uid, grp ? grp->gr_name : "",
(long)pwd->pw_gid, pwd->pw_gecos);
if (m_flag) {
for (i = 0, grp = grps; i < ngrps; ++i, ++grp) {
if (grp->gr_gid == pwd->pw_gid ||
!member(pwd->pw_name, grp->gr_mem))
continue;
printf(o_flag ? "%s:%s:%ld" : "%24s%-15s %-7ld\n",
"", grp->gr_name, (long)grp->gr_gid);
}
}
if (x_flag) {
printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_dir);
printf(o_flag ? "%s:%s" : "%24s%s\n", "", pwd->pw_shell);
}
if (a_flag) {
tm = gmtime(&pwd->pw_change);
strftime(cbuf, sizeof(cbuf), pwd->pw_change ? "%F" : "0", tm);
tm = gmtime(&pwd->pw_expire);
strftime(ebuf, sizeof(ebuf), pwd->pw_expire ? "%F" : "0", tm);
printf(o_flag ? "%s:%s:%s" : "%24s%s %s\n", "", cbuf, ebuf);
}
if (o_flag)
printf("\n");
}
static void
list_users(void)
{
struct xpasswd *pwd;
unsigned int i;
for (i = 0, pwd = pwds; i < npwds; ++i, ++pwd)
if (pwd->pw_selected)
display_user(pwd);
}
static void
usage(void)
{
fprintf(stderr, "usage: logins [-admopstux] [-g group] [-l login]\n");
exit(1);
}
int
main(int argc, char * const argv[])
{
int o;
while ((o = getopt(argc, argv, "adg:l:mopstux")) != -1)
switch (o) {
case 'a':
a_flag = 1;
break;
case 'd':
everything = 0;
d_flag = 1;
break;
case 'g':
everything = 0;
g_args = optarg;
break;
case 'l':
everything = 0;
l_args = optarg;
break;
case 'm':
m_flag = 1;
break;
case 'o':
o_flag = 1;
break;
case 'p':
everything = 0;
p_flag = 1;
break;
case 's':
everything = 0;
s_flag = 1;
break;
case 't':
t_flag = 1;
break;
case 'u':
everything = 0;
u_flag = 1;
break;
case 'x':
x_flag = 1;
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (argc > 0)
usage();
get_groups();
get_users();
select_users();
sort_users();
list_users();
exit(0);
}