]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lslogins(1): skeleton and argparsing for a new utility
authorOndrej Oprala <ooprala@redhat.com>
Fri, 4 Apr 2014 15:58:06 +0000 (17:58 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 15 May 2014 11:55:59 +0000 (13:55 +0200)
Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
configure.ac
sys-utils/Makemodule.am
sys-utils/lslogins.1 [new file with mode: 0644]
sys-utils/lslogins.c [new file with mode: 0644]

index c5a3ef5a3f62a5d42dda949f4d9cf680bdcd4889..b83df0d0693b590c8700ba389e857d369b3ce35c 100644 (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])
index 9d69bc60ecd17761e51b981d7542b6490f6fd7c0..2723768f92701032c38d97ac3b3c0f92342dd9c3 100644 (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
diff --git a/sys-utils/lslogins.1 b/sys-utils/lslogins.1
new file mode 100644 (file)
index 0000000..9b62cb8
--- /dev/null
@@ -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 .
diff --git a/sys-utils/lslogins.c b/sys-utils/lslogins.c
new file mode 100644 (file)
index 0000000..c9f812f
--- /dev/null
@@ -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;
+}