lslogins(1): skeleton and argparsing for a new utility

Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
This commit is contained in:
Ondrej Oprala 2014-04-04 17:58:06 +02:00 committed by Karel Zak
parent 37bc9a51c8
commit ab1cfad5b7
4 changed files with 516 additions and 0 deletions

View File

@ -1047,6 +1047,11 @@ UL_REQUIRES_HAVE([lscpu], [cpu_set_t], [cpu_set_t type])
AM_CONDITIONAL([BUILD_LSCPU], [test "x$build_lscpu" = xyes])
UL_BUILD_INIT([lslogins], [check])
UL_REQUIRES_BUILD([lslogins], [libsmartcols])
AM_CONDITIONAL([BUILD_LSLOGINS], [test "x$build_lslogins" = xyes])
UL_BUILD_INIT([chcpu], [check])
UL_REQUIRES_LINUX([chcpu])
UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type])

View File

@ -334,3 +334,13 @@ dist_man_MANS += sys-utils/setpriv.1
setpriv_SOURCES = sys-utils/setpriv.c
setpriv_LDADD = $(LDADD) -lcap-ng libcommon.la
endif
if BUILD_LSLOGINS
usrbin_exec_PROGRAMS += lslogins
dist_man_MANS += sys-utils/lslogins.1
lslogins_SOURCES = \
sys-utils/lslogins.c \
sys-utils/lslogins.h
lslogins_LDADD = $(LDADD) libcommon.la libsmartcols.la
lslogins_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
endif

118
sys-utils/lslogins.1 Normal file
View File

@ -0,0 +1,118 @@
.\" Copyright 2014 Ondrej Oprala (ondrej.oprala@gmail.com)
.\" May be distributed under the GNU General Public License
.TH LSLOGINS "1" "April 2014" "util-linux" "User Commands"
.SH NAME
lslogins \- display information about known users in the system
.SH SYNOPSIS
.B lslogins
[\fI-adehmoptvx\fR] [-s|-u[\fI=UID\fR]] [-g \fIGROUPS\fR] [-l \fILOGINS\fR]
.SH DESCRIPTION
.PP
Examine the wtmp and btmp logs, /etc/shadow (if necessary) and /etc/passwd
and output the desired data.
.PP
Mandatory arguments to long options are mandatory for short options too.
.PP
The default action is to list info about all the users in the system.
.SH OPTIONS
.PP
Display info about existing users.
.TP
\fB\-a\fR, \fB\-\-acc\-expiration\fR
Display data about the date of last password change and the account expiration date (see shadow(5) for more info).
.TP
\fB\-c\fR, \fB\-\-colon\fR
Separate info about each user with a colon instead of a newline.
.TP
\fB\-d\fR, \fB\-\-duplicates\fR
Show users with duplicate UIDs.
.TP
\fB\-e\fR, \fB\-\-export\fR
Output data in the format of NAME=VALUE.
.TP
\fB\-f\fR, \fB\-\-failed\fR
Display data about the users' last failed login attempts.
.TP
\fB\-g\fR, \fB\-\-groups\fR=\fIGROUPS\fR
Only show data of users belonging to \fIGROUPS\fR. More than one group may be specified; the list has to be comma-separated.
.TP
\fB\-\-last\fR
Display data containing information about the users' last login sessions.
.TP
\fB\-l\fR, \fB\-\-logins\fR=\fILOGINS\fR
Only show data of users with a login specified in \fILOGINS\fR. More than one login may be specified; the list has to be comma-separated.
.TP
\fB\-m\fR, \fB\-\-more\fR
Show secondary groups as well.
.TP
\fB\-n\fR, \fB\-\-newline\fR
Display each piece of information on a separate line.
.TP
\fB\-p\fR, \fB\-\-no\-password\fR
Show users without a password.
.TP
\fB\-r\fR, \fB\-\-raw\fR
Raw output (no columnation).
.TP
\fB\-s\fR, \fB\-\-sys\-accs\fR[=\fIUID\fR]
Show system accounts. These are by the default all accounts with UID below 1000 (non-inclusive), with the exception of either nobody or nfsnobody (UID 65534). The UID
treshold can also be specified explicitly (necessary for some distributions that allocate UIDs
starting from 100, 500 - or an entirely different value - rather than 1000).
.TP
\fB\-t\fR, \fB\-\-sort\fR
Sort by user name, rather than UID.
.TP
\fB\-u\fR, \fB\-\-user\-accs\fR[=\fIUID\fR]
Show user accounts. These are by the default all accounts with UID above 1000 (inclusive), with the exception of either nobody or nfsnobody (UID 65534). The UID
treshold can also be specified explicitly (necessary for some distributions that allocate UIDs
starting from 100, 500 - or an entirely different value - rather than 1000).
.TP
\fB\-x\fR, \fB\-\-extra\fR
Show extra information about users - home diretory, default login shell, password age and expiry information.
.TP
\fB\-z\fR, \fB\-\-print0\fR
Delimit user entries with a nul character, instead of a newline.
.TP
\fB\-h\fR, \fB\-\-help\fR
Display help information and exit.
\fB\-v\fR, \fB\-\-version\fR
Display version information and exit.
Note that switches -a and -x require root priviliges. Otherwise their fields will state <unknown> for each entry.
.sp
.SH COLORS
Implicit coloring can be disabled as follows:
.RS
.br
.BI "touch /etc/terminal-colors.d/lslogins.disable"
.br
.RE
For more details see
.BR terminal-colors.d (5).
.SH EXIT STATUS
.TP
0
if OK,
.TP
1
if incorrect arguments specified,
.TP
2
if a serious error occurs (e.g. a corrupt log).
.SH SEE ALSO
\fBgroup\fP(5), \fBpasswd\fP(5), \fBshadow\fP(5), \fButmp\fP(5)
.SH HISTORY
The \fBlslogins\fP utility is inspired by the \fBlogins\fP utility, which first appeared in FreeBSD 4.10.
.SH AUTHORS
.MT ondrej.oprala@gmail.com
Ondrej Oprala
.ME
.SH AVAILABILITY
The lslogins command is part of the util-linux package and is available from
.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
Linux Kernel Archive
.UE .

383
sys-utils/lslogins.c Normal file
View File

@ -0,0 +1,383 @@
/*
* 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;
}