util-linux/login-utils/lslogins.c

384 lines
10 KiB
C
Raw Normal View History

/*
* lslogins - List information about users on the system
*
* Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <pwd.h>
#include <shadow.h>
#include <paths.h>
#include <time.h>
#include "c.h"
#include "nls.h"
#include "closestream.h"
#include "xalloc.h"
#include "strutils.h"
#include "optutils.h"
/*
* column description
*/
struct lslogins_coldesc {
const char *name;
const char *help;
unsigned int is_abbr:1; /* name is abbreviation */
};
/* the most uber of flags */
static int uberflag;
/* we use the value of outmode to determine
* appropriate flags for the libsmartcols table
* (e.g., a value of out_newline would imply a raw
* table with the column separator set to '\n').
*/
static int outmode;
/*
* output modes
*/
enum {
out_colon = 0,
out_export,
out_newline,
out_raw,
out_nul,
};
struct lslogins_user {
char *login;
uid_t uid;
char *group;
gid_t gid;
char *gecos;
int nopasswd:1;
char *sgroups;
struct tm *pwd_ctime;
struct tm *pwd_expir;
struct tm *last_login;
char * last_tty;
char * last_hostname;
struct tm *failed_login;
struct tm *failed_tty;
char *homedir;
char *shell;
char *pwd_status;
char *hush_status;
};
/*
* flags
*/
enum {
F_EXPIR = (1 << 0),
F_DUP = (1 << 1),
F_EXPRT = (1 << 2),
F_MORE = (1 << 3),
F_NOPWD = (1 << 4),
F_SYSAC = (1 << 5),
F_USRAC = (1 << 6),
F_SORT = (1 << 7),
F_EXTRA = (1 << 8),
F_FAIL = (1 << 9),
F_LAST = (1 << 10),
};
/*
* IDs
*/
enum {
COL_LOGIN = 0,
COL_UID,
COL_NOPASSWD,
COL_PGRP,
COL_PGID,
COL_SGRPS,
COL_HOME,
COL_SHELL,
COL_FULLNAME,
COL_LAST_LOGIN,
COL_LAST_TTY,
COL_LAST_HOSTNAME,
COL_FAILED_LOGIN,
COL_FAILED_TTY,
COL_HUSH_STATUS,
COL_PWD_STATUS,
COL_PWD_EXPIR,
COL_PWD_CTIME,
/*COL_PWD_CTIME_MAX,
COL_PWD_CTIME_MIN,*/
};
static struct lslogins_coldesc coldescs[] =
{
[COL_LOGIN] = { "LOGIN", N_("user/system login"), 1 },
[COL_UID] = { "UID", N_("user UID") },
[COL_NOPASSWD] = { "HAS PASSWORD", N_("account has a password?") },
[COL_PGRP] = { "GRP", N_("primary group name") },
[COL_PGID] = { "GRP_GID", N_("primary group GID") },
[COL_SGRPS] = { "SEC_GRPS", N_("secondary group names and GIDs") },
[COL_HOME] = { "HOMEDIR", N_("home directory") },
[COL_SHELL] = { "SHELL", N_("login shell") },
[COL_FULLNAME] = { "FULLNAME", N_("full user name") },
[COL_LAST_LOGIN] = { "LAST_LOGIN", N_("date of last login") },
[COL_LAST_TTY] = { "LAST_TTY", N_("last tty used") },
[COL_LAST_HOSTNAME] = { "LAST_HOSTNAME", N_("hostname during the last session") },
[COL_FAILED_LOGIN] = { "FAILED_LOGIN", N_("date of last failed login") },
[COL_FAILED_TTY] = { "FAILED_TTY", N_("where did the login fail?") },
[COL_HUSH_STATUS] = { "HUSH_STATUS", N_("User's hush settings") },
[COL_PWD_STATUS] = { "PWD_STATUS", N_("password status - see the -x option description") },
[COL_PWD_EXPIR] = { "PWD_EXPIR", N_("password expiration date") },
[COL_PWD_CTIME] = { "PWD_CHANGE", N_("date of last password change") },
/*[COL_PWD_CTIME_MAX] = { "PWD UNTIL", N_("max number of days a password may remain unchanged") },
[COL_PWD_CTIME_MIN] = { "PWD CAN CHANGE", N_("number of days required between changes") },*/
};
static int
column_name_to_id(const char *name, size_t namesz)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
const char *cn = coldescs[i].name;
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
return i;
}
warnx(_("unknown column: %s"), name);
return -1;
}
static void __attribute__((__noreturn__)) usage(FILE *out)
{
size_t i;
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -a, --acc-expiration Display data\n"), out);
fputs(_(" -c, --colon-separate Display data in a format similar to /etc/passwd\n"), out);
fputs(_(" -d, --duplicates Display users with duplicate UIDs\n"), out);
fputs(_(" -e, --export Display in an export-able output format\n"), out);
fputs(_(" -f, --failed Display data about the last users' failed logins\n"), out);
fputs(_(" -g, --groups=<GROUPS> Display users belonging to a group in GROUPS\n"), out);
fputs(_(" -l, --logins=<LOGINS> Display only users from LOGINS\n"), out);
fputs(_(" --last Show info about the last login sessions\n"), out);
fputs(_(" -m, --more Display secondary groups as well\n"), out);
fputs(_(" -n, --newline Display each piece of information on a new line\n"), out);
fputs(_(" -o, --output[=<LIST>] Define the columns to output\n"), out);
fputs(_(" -p, --no-password Display users without a password\n"), out);
fputs(_(" -r, --raw Display the raw table\n"), out);
fputs(_(" -s, --sys-accs[=<UID>] Display system accounts\n"), out);
fputs(_(" -t, --sort Sort output by login instead of UID\n"), out);
fputs(_(" -u, --user-accs[=<UID>] Display user accounts\n"), out);
fputs(_(" -x, --extra Display extra information\n"), out);
fputs(_(" -z, --print0 Delimit user entries with a nul character"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fprintf(out, _("\nAvailable columns:\n"));
for (i = 0; i < ARRAY_SIZE(coldescs); i++)
fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help));
fprintf(out, _("\nFor more details see lslogins(1).\n"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
int c;
int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
char *logins = NULL, *groups = NULL;
/* long only options. */
enum {
OPT_LAST = CHAR_MAX + 1,
OPT_VER,
};
static const struct option longopts[] = {
{ "acc-expiration", no_argument, 0, 'a' },
{ "colon", no_argument, 0, 'c' },
{ "duplicates", no_argument, 0, 'd' },
{ "export", no_argument, 0, 'e' },
{ "failed", no_argument, 0, 'f' },
{ "groups", required_argument, 0, 'g' },
{ "help", no_argument, 0, 'h' },
{ "logins", required_argument, 0, 'l' },
{ "more", no_argument, 0, 'm' },
{ "newline", no_argument, 0, 'n' },
{ "output", optional_argument, 0, 'o' },
{ "no-password", no_argument, 0, 'p' },
{ "last", no_argument, 0, OPT_LAST },
{ "raw", no_argument, 0, 'r' },
{ "sys-accs", optional_argument, 0, 's' },
{ "sort", no_argument, 0, 't' },
{ "user-accs", optional_argument, 0, 'u' },
{ "version", no_argument, 0, OPT_VER },
{ "extra", no_argument, 0, 'x' },
{ "print0", no_argument, 0, 'z' },
{ NULL, 0, 0, 0 }
};
static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
{ 'c','e','n','r','z' },
{ 0 }
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
while ((c = getopt_long(argc, argv, "acdefg:hl:mno::prs::tu::xz",
longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch (c) {
case 'a':
uberflag |= F_EXPIR;
break;
case 'c':
outmode = out_colon;
break;
case 'd':
uberflag |= F_DUP;
break;
case 'e':
outmode = out_export;
break;
case 'f':
uberflag |= F_FAIL;
break;
case 'g':
groups = strdup(optarg);
if (!groups)
return EXIT_FAILURE;
break;
case 'h':
usage(stdout);
case 'l':
logins = strdup(optarg);
if (!logins)
return EXIT_FAILURE;
break;
case 'm':
uberflag |= F_MORE;
break;
case 'n':
outmode = out_newline;
break;
case 'o':
if (optarg) {
if (*optarg == '=')
optarg++;
ncolumns = string_to_idarray(optarg,
columns, ARRAY_SIZE(columns),
column_name_to_id);
if (ncolumns < 0)
return EXIT_FAILURE;
}
break;
case 'p':
uberflag |= F_NOPWD;
break;
case 'r':
outmode = out_raw;
break;
case OPT_LAST:
uberflag |= F_LAST;
break;
case 's':
uberflag |= F_SYSAC;
break;
case 't':
uberflag |= F_SORT;
break;
case 'u':
uberflag |= F_USRAC;
break;
case OPT_VER:
printf(_("%s from %s\n"), program_invocation_short_name,
PACKAGE_STRING);
return EXIT_SUCCESS;
case 'x':
uberflag |= F_EXTRA;
break;
case 'z':
outmode = out_nul;
break;
default:
usage(stderr);
}
}
if (argc != optind)
usage(stderr);
if (!ncolumns) {
columns[ncolumns++] = COL_LOGIN;
columns[ncolumns++] = COL_UID;
columns[ncolumns++] = COL_PGRP;
columns[ncolumns++] = COL_PGID;
columns[ncolumns++] = COL_FULLNAME;
if (uberflag & F_NOPWD) {
columns[ncolumns++] = COL_NOPASSWD;
}
if (uberflag & F_MORE) {
columns[ncolumns++] = COL_SGRPS;
}
if (uberflag & F_EXPIR) {
columns[ncolumns++] = COL_PWD_CTIME;
columns[ncolumns++] = COL_PWD_EXPIR;
}
if (uberflag & F_LAST) {
columns[ncolumns++] = COL_LAST_LOGIN;
columns[ncolumns++] = COL_LAST_TTY;
columns[ncolumns++] = COL_LAST_HOSTNAME;
}
if (uberflag & F_FAIL) {
columns[ncolumns++] = COL_FAILED_LOGIN;
columns[ncolumns++] = COL_FAILED_TTY;
}
if (uberflag & F_EXTRA) {
columns[ncolumns++] = COL_HOME;
columns[ncolumns++] = COL_SHELL;
columns[ncolumns++] = COL_PWD_STATUS;
columns[ncolumns++] = COL_HUSH_STATUS;
/* columns[ncolumns++] = COL_PWD_CTIME_MAX;
columns[ncolumns++] = COL_PWD_CTIME_MIN; */
}
}
return EXIT_SUCCESS;
}