#include <shadow.h>
#include <paths.h>
#include <time.h>
-#include <utmp.h>
+#include <utmpx.h>
#include <signal.h>
#include <err.h>
#include <limits.h>
-
#include <search.h>
#include <libsmartcols.h>
#include "optutils.h"
#include "pathnames.h"
#include "logindefs.h"
-#include "readutmp.h"
#include "procutils.h"
+#include "timeutils.h"
/*
* column description
#define UL_UID_MIN 1000
#define UL_UID_MAX 60000
-#define UL_SYS_UID_MIN 201
+#define UL_SYS_UID_MIN 101
#define UL_SYS_UID_MAX 999
/* we use the value of outmode to determine
char *pwd_expire;
char *pwd_ctime_min;
char *pwd_ctime_max;
+ const char *pwd_method;
char *last_login;
char *last_tty;
TIME_SHORT,
TIME_FULL,
TIME_ISO,
+ TIME_ISO_SHORT,
};
/*
COL_PWDLOCK,
COL_PWDEMPTY,
COL_PWDDENY,
+ COL_PWDMETHOD,
COL_GROUP,
COL_GID,
COL_SGROUPS,
[COL_PWDEMPTY] = { "PWD-EMPTY", N_("password not required"), N_("Password not required"), 1, SCOLS_FL_RIGHT },
[COL_PWDDENY] = { "PWD-DENY", N_("login by password disabled"), N_("Login by password disabled"), 1, SCOLS_FL_RIGHT },
[COL_PWDLOCK] = { "PWD-LOCK", N_("password defined, but locked"), N_("Password is locked"), 1, SCOLS_FL_RIGHT },
+ [COL_PWDMETHOD] = { "PWD-METHOD", N_("password encryption method"), N_("Password encryption method"), 0.1 },
[COL_NOLOGIN] = { "NOLOGIN", N_("log in disabled by nologin(8) or pam_nologin(8)"), N_("No login"), 1, SCOLS_FL_RIGHT },
[COL_GROUP] = { "GROUP", N_("primary group name"), N_("Primary group"), 0.1 },
[COL_GID] = { "GID", N_("primary group ID"), "GID", 1, SCOLS_FL_RIGHT },
};
struct lslogins_control {
- struct utmp *wtmp;
+ struct utmpx *wtmp;
size_t wtmp_size;
- struct utmp *btmp;
+ struct utmpx *btmp;
size_t btmp_size;
void *usertree;
const char *journal_path;
unsigned int selinux_enabled : 1,
+ fail_on_unknown : 1, /* fail if user does not exist */
+ ulist_on : 1,
noheadings : 1,
notrunc : 1;
};
* column twice. That's enough, dynamically allocated array of the columns is
* unnecessary overkill and over-engineering in this case */
static int columns[ARRAY_SIZE(coldescs) * 2];
-static int ncolumns;
+static size_t ncolumns;
static inline size_t err_columns_index(size_t arysz, size_t idx)
{
if (idx >= arysz)
errx(EXIT_FAILURE, _("too many columns specified, "
- "the limit is %zu columns."),
+ "the limit is %zu columns"),
arysz - 1);
return idx;
}
#define add_column(ary, n, id) \
((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
-static struct timeval now;
-
-static int date_is_today(time_t t)
-{
- if (now.tv_sec == 0)
- gettimeofday(&now, NULL);
- return t / (3600 * 24) == now.tv_sec / (3600 * 24);
-}
-
-static int date_is_thisyear(time_t t)
-{
- if (now.tv_sec == 0)
- gettimeofday(&now, NULL);
- return t / (3600 * 24 * 365) == now.tv_sec / (3600 * 24 * 365);
-}
-
static int column_name_to_id(const char *name, size_t namesz)
{
size_t i;
return -1;
}
+static struct timeval now;
+
static char *make_time(int mode, time_t time)
{
- char *s;
- struct tm tm;
+ int rc = 0;
char buf[64] = {0};
- localtime_r(&time, &tm);
-
switch(mode) {
case TIME_FULL:
+ {
+ char *s;
+ struct tm tm;
+ localtime_r(&time, &tm);
+
asctime_r(&tm, buf);
if (*(s = buf + strlen(buf) - 1) == '\n')
*s = '\0';
+ rc = 0;
break;
+ }
case TIME_SHORT:
- if (date_is_today(time))
- strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
- else if (date_is_thisyear(time))
- strftime(buf, sizeof(buf), "%b%d/%H:%M", &tm);
- else
- strftime(buf, sizeof(buf), "%Y-%b%d", &tm);
+ rc = strtime_short(&time, &now, UL_SHORTTIME_THISYEAR_HHMM,
+ buf, sizeof(buf));
break;
case TIME_ISO:
- strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm);
+ rc = strtime_iso(&time, ISO_TIMESTAMP_T, buf, sizeof(buf));
+ break;
+ case TIME_ISO_SHORT:
+ rc = strtime_iso(&time, ISO_DATE, buf, sizeof(buf));
break;
default:
- errx(EXIT_FAILURE, _("unssupported time type"));
+ errx(EXIT_FAILURE, _("unsupported time type"));
}
+
+ if (rc)
+ errx(EXIT_FAILURE, _("failed to compose time string"));
+
return xstrdup(buf);
}
x = snprintf(p, len, "%s,", grp->gr_name);
}
- if (x < 0 || (size_t) x + 1 > len) {
+ if (x < 0 || (size_t) x >= len) {
size_t cur = p - res;
maxlen *= 2;
return res;
}
-static struct utmp *get_last_wtmp(struct lslogins_control *ctl, const char *username)
+static struct utmpx *get_last_wtmp(struct lslogins_control *ctl, const char *username)
{
size_t n = 0;
- size_t len;
if (!username)
return NULL;
- len = strlen(username);
n = ctl->wtmp_size - 1;
do {
if (!strncmp(username, ctl->wtmp[n].ut_user,
- len < UT_NAMESIZE ? len : UT_NAMESIZE))
+ sizeof(ctl->wtmp[0].ut_user)))
return ctl->wtmp + n;
} while (n--);
return NULL;
static int require_wtmp(void)
{
size_t i;
- for (i = 0; i < (size_t) ncolumns; i++)
+ for (i = 0; i < ncolumns; i++)
if (is_wtmp_col(columns[i]))
return 1;
return 0;
static int require_btmp(void)
{
size_t i;
- for (i = 0; i < (size_t) ncolumns; i++)
+ for (i = 0; i < ncolumns; i++)
if (is_btmp_col(columns[i]))
return 1;
return 0;
}
-static struct utmp *get_last_btmp(struct lslogins_control *ctl, const char *username)
+static struct utmpx *get_last_btmp(struct lslogins_control *ctl, const char *username)
{
size_t n = 0;
- size_t len;
if (!username)
return NULL;
- len = strlen(username);
n = ctl->btmp_size - 1;
do {
if (!strncmp(username, ctl->btmp[n].ut_user,
- len < UT_NAMESIZE ? len : UT_NAMESIZE))
+ sizeof(ctl->wtmp[0].ut_user)))
return ctl->btmp + n;
}while (n--);
return NULL;
}
+static int read_utmp(char const *file, size_t *nents, struct utmpx **res)
+{
+ size_t n_read = 0, n_alloc = 0;
+ struct utmpx *utmp = NULL, *u;
+
+ if (utmpxname(file) < 0)
+ return -errno;
+
+ setutxent();
+ errno = 0;
+
+ while ((u = getutxent()) != NULL) {
+ if (n_read == n_alloc) {
+ n_alloc += 32;
+ utmp = xrealloc(utmp, n_alloc * sizeof (struct utmpx));
+ }
+ utmp[n_read++] = *u;
+ }
+ if (!u && errno) {
+ free(utmp);
+ return -errno;
+ }
+
+ endutxent();
+
+ *nents = n_read;
+ *res = utmp;
+
+ return 0;
+}
+
static int parse_wtmp(struct lslogins_control *ctl, char *path)
{
int rc = 0;
static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd)
{
size_t n = 0;
+ int ngroups = 0;
*len = 0;
*list = NULL;
/* first let's get a supp. group count */
- getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len);
- if (!*len)
+ getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups);
+ if (!ngroups)
return -1;
- *list = xcalloc(1, *len * sizeof(gid_t));
+ *list = xcalloc(1, ngroups * sizeof(gid_t));
/* now for the actual list of GIDs */
- if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len))
+ if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups))
return -1;
+ *len = (size_t) ngroups;
+
/* getgroups also returns the user's primary GID - dispose of it */
while (n < *len) {
if ((*list)[n] == pwd->pw_gid)
if (*len)
(*list)[n] = (*list)[--(*len)];
+
return 0;
}
return nprocs;
}
+static const char *get_pwd_method(const char *str, const char **next, unsigned int *sz)
+{
+ const char *p = str;
+ const char *res = NULL;
+
+ if (!p || *p++ != '$')
+ return NULL;
+
+ if (sz)
+ *sz = 0;
+
+ switch (*p) {
+ case '1':
+ res = "MD5";
+ if (sz)
+ *sz = 22;
+ break;
+ case '2':
+ p++;
+ if (*p == 'a' || *p == 'y')
+ res = "Blowfish";
+ break;
+ case '5':
+ res = "SHA-256";
+ if (sz)
+ *sz = 43;
+ break;
+ case '6':
+ res = "SHA-512";
+ if (sz)
+ *sz = 86;
+ break;
+ default:
+ return NULL;
+ }
+ p++;
+
+ if (*p != '$')
+ return NULL;
+ if (next)
+ *next = ++p;
+ return res;
+}
+
+#define is_valid_pwd_char(x) (isalnum((unsigned char) (x)) || (x) == '.' || (x) == '/')
+
+/*
+ * This function do not accept empty passwords or locked accouns.
+ */
static int valid_pwd(const char *str)
{
- const char *p;
+ const char *p = str;
+ unsigned int sz = 0, n;
+
+ if (!str || !*str)
+ return 0;
- for (p = str; p && *p; p++)
- if (!isalnum((unsigned int) *p))
+ /* $id$ */
+ if (get_pwd_method(str, &p, &sz) == NULL)
+ return 0;
+ if (!p || !*p)
+ return 0;
+
+ /* salt$ */
+ for (; *p; p++) {
+ if (*p == '$') {
+ p++;
+ break;
+ }
+ if (!is_valid_pwd_char(*p))
+ return 0;
+ }
+ if (!*p)
+ return 0;
+
+ /* encrypted */
+ for (n = 0; *p; p++, n++) {
+ if (!is_valid_pwd_char(*p))
return 0;
- return p > str ? 1 : 0;
+ }
+
+ if (sz && n != sz)
+ return 0;
+ return 1;
}
static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const char *username)
struct passwd *pwd;
struct group *grp;
struct spwd *shadow;
- struct utmp *user_wtmp = NULL, *user_btmp = NULL;
- int n = 0;
+ struct utmpx *user_wtmp = NULL, *user_btmp = NULL;
+ size_t n = 0;
time_t time;
uid_t uid;
errno = 0;
+ errno = 0;
pwd = username ? getpwnam(username) : getpwent();
if (!pwd)
return NULL;
return NULL;
}
- user = xcalloc(1, sizeof(struct lslogins_user));
-
+ errno = 0;
grp = getgrgid(pwd->pw_gid);
if (!grp)
return NULL;
+ user = xcalloc(1, sizeof(struct lslogins_user));
+
if (ctl->wtmp)
user_wtmp = get_last_wtmp(ctl, pwd->pw_name);
if (ctl->btmp)
} else
user->pwd_deny = STATUS_UNKNOWN;
break;
-
case COL_PWDLOCK:
if (shadow) {
if (*shadow->sp_pwdp == '!' && valid_pwd(shadow->sp_pwdp + 1))
} else
user->pwd_lock = STATUS_UNKNOWN;
break;
+ case COL_PWDMETHOD:
+ if (shadow) {
+ const char *p = shadow->sp_pwdp;
+
+ if (*p == '!' || *p == '*')
+ p++;
+ user->pwd_method = get_pwd_method(p, NULL, NULL);
+ } else
+ user->pwd_method = NULL;
+ break;
case COL_NOLOGIN:
if (strstr(pwd->pw_shell, "nologin"))
user->nologin = 1;
else if (pwd->pw_uid)
- user->nologin = access("/etc/nologin", F_OK) == 0 ||
- access("/var/run/nologin", F_OK) == 0;
+ user->nologin = access(_PATH_NOLOGIN, F_OK) == 0 ||
+ access(_PATH_VAR_NOLOGIN, F_OK) == 0;
break;
case COL_PWD_WARN:
if (shadow && shadow->sp_warn >= 0)
break;
case COL_PWD_EXPIR:
if (shadow && shadow->sp_expire >= 0)
- user->pwd_expire = make_time(TIME_SHORT,
+ user->pwd_expire = make_time(ctl->time_mode == TIME_ISO ?
+ TIME_ISO_SHORT : ctl->time_mode,
shadow->sp_expire * 86400);
break;
case COL_PWD_CTIME:
* (especially in non-GMT timezones) would only serve
* to confuse */
if (shadow)
- user->pwd_ctime = make_time(TIME_SHORT,
+ user->pwd_ctime = make_time(ctl->time_mode == TIME_ISO ?
+ TIME_ISO_SHORT : ctl->time_mode,
shadow->sp_lstchg * 86400);
break;
case COL_PWD_CTIME_MIN:
return user;
}
-/* some UNIX implementations set errno iff a passwd/grp/...
- * entry was not found. The original UNIX logins(1) utility always
- * ignores invalid login/group names, so we're going to as well.*/
-#define IS_REAL_ERRNO(e) !((e) == ENOENT || (e) == ESRCH || \
- (e) == EBADF || (e) == EPERM || (e) == EAGAIN)
-
-/* get a definitive list of users we want info about... */
-
static int str_to_uint(char *s, unsigned int *ul)
{
char *end;
return 1;
}
+/* get a definitive list of users we want info about... */
static int get_ulist(struct lslogins_control *ctl, char *logins, char *groups)
{
char *u, *g;
*arsiz = 32;
*ar = xcalloc(1, sizeof(char *) * (*arsiz));
- while ((u = strtok(logins, ","))) {
- logins = NULL;
+ if (logins) {
+ while ((u = strtok(logins, ","))) {
+ logins = NULL;
- /* user specified by UID? */
- if (!str_to_uint(u, &uid)) {
- pwd = getpwuid(uid);
- if (!pwd)
- continue;
- u = pwd->pw_name;
- }
- (*ar)[i++] = xstrdup(u);
-
- if (i == *arsiz)
- *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32));
- }
- /* FIXME: this might lead to duplicit entries, although not visible
- * in output, crunching a user's info multiple times is very redundant */
- while ((g = strtok(groups, ","))) {
- groups = NULL;
-
- /* user specified by GID? */
- if (!str_to_uint(g, &gid))
- grp = getgrgid(gid);
- else
- grp = getgrnam(g);
-
- if (!grp)
- continue;
-
- while ((u = grp->gr_mem[n++])) {
+ /* user specified by UID? */
+ if (!str_to_uint(u, &uid)) {
+ pwd = getpwuid(uid);
+ if (!pwd)
+ continue;
+ u = pwd->pw_name;
+ }
(*ar)[i++] = xstrdup(u);
if (i == *arsiz)
*ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32));
}
+ ctl->ulist_on = 1;
+ }
+
+ if (groups) {
+ /* FIXME: this might lead to duplicate entries, although not visible
+ * in output, crunching a user's info multiple times is very redundant */
+ while ((g = strtok(groups, ","))) {
+ n = 0;
+ groups = NULL;
+
+ /* user specified by GID? */
+ if (!str_to_uint(g, &gid))
+ grp = getgrgid(gid);
+ else
+ grp = getgrnam(g);
+
+ if (!grp)
+ continue;
+
+ while ((u = grp->gr_mem[n++])) {
+ (*ar)[i++] = xstrdup(u);
+
+ if (i == *arsiz)
+ *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32));
+ }
+ }
+ ctl->ulist_on = 1;
}
*arsiz = i;
return 0;
return u;
}
+/* some UNIX implementations set errno iff a passwd/grp/...
+ * entry was not found. The original UNIX logins(1) utility always
+ * ignores invalid login/group names, so we're going to as well.*/
+#define IS_REAL_ERRNO(e) !((e) == ENOENT || (e) == ESRCH || \
+ (e) == EBADF || (e) == EPERM || (e) == EAGAIN)
+
static int get_user(struct lslogins_control *ctl, struct lslogins_user **user,
const char *username)
{
*user = get_user_info(ctl, username);
- if (!*user && errno)
- if (IS_REAL_ERRNO(errno))
- return -1;
+ if (!*user && IS_REAL_ERRNO(errno))
+ return -1;
return 0;
}
static int cmp_uid(const void *a, const void *b)
{
- uid_t x = ((struct lslogins_user *)a)->uid;
- uid_t z = ((struct lslogins_user *)b)->uid;
+ uid_t x = ((const struct lslogins_user *)a)->uid;
+ uid_t z = ((const struct lslogins_user *)b)->uid;
return x > z ? 1 : (x < z ? -1 : 0);
}
struct lslogins_user *user = NULL;
size_t n = 0;
- if (*ctl->ulist) {
- while (n < ctl->ulsiz) {
- if (get_user(ctl, &user, ctl->ulist[n]))
+ if (ctl->ulist_on) {
+ for (n = 0; n < ctl->ulsiz; n++) {
+ int rc = get_user(ctl, &user, ctl->ulist[n]);
+
+ if (ctl->fail_on_unknown && !user) {
+ warnx(_("cannot found '%s'"), ctl->ulist[n]);
return -1;
- if (user) /* otherwise an invalid user name has probably been given */
- tsearch(user, &ctl->usertree, cmp_uid);
- ++n;
+ }
+ if (rc || !user)
+ continue;
+
+ tsearch(user, &ctl->usertree, cmp_uid);
}
} else {
while ((user = get_next_user(ctl)))
static struct libscols_table *setup_table(struct lslogins_control *ctl)
{
- struct libscols_table *tb = scols_new_table();
- int n = 0;
+ struct libscols_table *table = scols_new_table();
+ size_t n = 0;
- if (!tb)
- errx(EXIT_FAILURE, _("failed to initialize output table"));
+ if (!table)
+ err(EXIT_FAILURE, _("failed to allocate output table"));
if (ctl->noheadings)
- scols_table_enable_noheadings(tb, 1);
+ scols_table_enable_noheadings(table, 1);
switch(outmode) {
case OUT_COLON:
- scols_table_enable_raw(tb, 1);
- scols_table_set_column_separator(tb, ":");
+ scols_table_enable_raw(table, 1);
+ scols_table_set_column_separator(table, ":");
break;
case OUT_NEWLINE:
- scols_table_set_column_separator(tb, "\n");
+ scols_table_set_column_separator(table, "\n");
/* fallthrough */
case OUT_EXPORT:
- scols_table_enable_export(tb, 1);
+ scols_table_enable_export(table, 1);
break;
case OUT_NUL:
- scols_table_set_line_separator(tb, "\0");
+ scols_table_set_line_separator(table, "\0");
/* fallthrough */
case OUT_RAW:
- scols_table_enable_raw(tb, 1);
+ scols_table_enable_raw(table, 1);
break;
case OUT_PRETTY:
- scols_table_enable_noheadings(tb, 1);
+ scols_table_enable_noheadings(table, 1);
default:
break;
}
if (ctl->notrunc)
flags &= ~SCOLS_FL_TRUNC;
- if (!scols_table_new_column(tb,
+ if (!scols_table_new_column(table,
coldescs[columns[n]].name,
coldescs[columns[n]].whint,
flags))
++n;
}
- return tb;
+ return table;
fail:
- scols_unref_table(tb);
+ scols_unref_table(table);
return NULL;
}
static void fill_table(const void *u, const VISIT which, const int depth __attribute__((unused)))
{
struct libscols_line *ln;
- struct lslogins_user *user = *(struct lslogins_user **)u;
- int n = 0;
+ const struct lslogins_user *user = *(struct lslogins_user * const *)u;
+ size_t n = 0;
if (which == preorder || which == endorder)
return;
ln = scols_table_new_line(tb, NULL);
+ if (!ln)
+ err(EXIT_FAILURE, _("failed to allocate output line"));
+
while (n < ncolumns) {
int rc = 0;
case COL_PWDDENY:
rc = scols_line_set_data(ln, n, get_status(user->pwd_deny));
break;
+ case COL_PWDMETHOD:
+ rc = scols_line_set_data(ln, n, user->pwd_method);
+ break;
case COL_GROUP:
rc = scols_line_set_data(ln, n, user->group);
break;
err(EXIT_FAILURE, _("internal error: unknown column"));
}
- if (rc != 0)
- err(EXIT_FAILURE, _("failed to set data"));
+ if (rc)
+ err(EXIT_FAILURE, _("failed to add output data"));
++n;
}
return;
}
#ifdef HAVE_LIBSYSTEMD
-static void print_journal_tail(const char *journal_path, uid_t uid, size_t len)
+static void print_journal_tail(const char *journal_path, uid_t uid, size_t len, int time_mode)
{
sd_journal *j;
- char *match, *buf;
+ char *match, *timestamp;
uint64_t x;
time_t t;
const char *identifier, *pid, *message;
else
sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
- buf = xmalloc(sizeof(char) * 16);
xasprintf(&match, "_UID=%d", uid);
sd_journal_add_match(j, match, 0);
do {
if (0 > sd_journal_get_data(j, "SYSLOG_IDENTIFIER",
(const void **) &identifier, &identifier_len))
- return;
+ goto done;
if (0 > sd_journal_get_data(j, "_PID",
(const void **) &pid, &pid_len))
- return;
+ goto done;
if (0 > sd_journal_get_data(j, "MESSAGE",
(const void **) &message, &message_len))
- return;
+ goto done;
sd_journal_get_realtime_usec(j, &x);
t = x / 1000000;
- strftime(buf, 16, "%b %d %H:%M:%S", localtime(&t));
-
- fprintf(stdout, "%s", buf);
-
+ timestamp = make_time(time_mode, t);
+ /* Get rid of journal entry field identifiers */
identifier = strchr(identifier, '=') + 1;
- pid = strchr(pid, '=') + 1 ;
+ pid = strchr(pid, '=') + 1;
message = strchr(message, '=') + 1;
- fprintf(stdout, " %s", identifier);
- fprintf(stdout, "[%s]:", pid);
- fprintf(stdout, "%s\n", message);
+ fprintf(stdout, "%s %s[%s]: %s\n", timestamp, identifier, pid,
+ message);
+ free(timestamp);
} while (sd_journal_next(j));
- free(buf);
+done:
free(match);
sd_journal_flush_matches(j);
sd_journal_close(j);
}
#endif
-static int print_pretty(struct libscols_table *tb)
+static int print_pretty(struct libscols_table *table)
{
struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
struct libscols_column *col;
const char *hstr, *dstr;
int n = 0;
- ln = scols_table_get_line(tb, 0);
- while (!scols_table_next_column(tb, itr, &col)) {
+ ln = scols_table_get_line(table, 0);
+ while (!scols_table_next_column(table, itr, &col)) {
data = scols_line_get_cell(ln, n);
print_pretty(tb);
#ifdef HAVE_LIBSYSTEMD
fprintf(stdout, _("\nLast logs:\n"));
- print_journal_tail(ctl->journal_path, ctl->uid, 3);
+ print_journal_tail(ctl->journal_path, ctl->uid, 3, ctl->time_mode);
fputc('\n', stdout);
#endif
} else
free(u);
}
-struct lslogins_timefmt {
- const char *name;
- int val;
-};
+static int parse_time_mode(const char *s)
+{
+ struct lslogins_timefmt {
+ const char *name;
+ const int val;
+ };
+ static const struct lslogins_timefmt timefmts[] = {
+ {"iso", TIME_ISO},
+ {"full", TIME_FULL},
+ {"short", TIME_SHORT},
+ };
+ size_t i;
-static struct lslogins_timefmt timefmts[] = {
- { "short", TIME_SHORT },
- { "full", TIME_FULL },
- { "iso", TIME_ISO },
-};
+ for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
+ if (strcmp(timefmts[i].name, s) == 0)
+ return timefmts[i].val;
+ }
+ errx(EXIT_FAILURE, _("unknown time format: %s"), s);
+}
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
size_t i;
fputs(USAGE_HEADER, out);
- fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
+ fprintf(out, _(" %s [options] [<username>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Display information about known users in the system.\n"), out);
fputs(USAGE_OPTIONS, out);
fputs(_(" -a, --acc-expiration display info about passwords expiration\n"), out);
fputs(_(" -c, --colon-separate display data in a format similar to /etc/passwd\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-info display information about groups\n"), out);
+ fputs(_(" -f, --failed display data about the users' last failed logins\n"), out);
+ fputs(_(" -G, --supp-groups display information about groups\n"), out);
fputs(_(" -g, --groups=<groups> display users belonging to a group in <groups>\n"), out);
fputs(_(" -L, --last show info about the users' last login sessions\n"), out);
fputs(_(" -l, --logins=<logins> display only users from <logins>\n"), out);
- fputs(_(" -m, --supp-groups display supplementary groups as well\n"), out);
fputs(_(" -n, --newline display each piece of information on a new line\n"), out);
fputs(_(" --noheadings don't print headings\n"), out);
fputs(_(" --notruncate don't truncate output\n"), out);
fputs(_(" -o, --output[=<list>] define the columns to output\n"), out);
+ fputs(_(" --output-all output all columns\n"), out);
fputs(_(" -p, --pwd display information related to login by password.\n"), out);
fputs(_(" -r, --raw display in raw mode\n"), out);
fputs(_(" -s, --system-accs display system accounts\n"), out);
fputs(_(" --wtmp-file <path> set an alternate path for wtmp\n"), out);
fputs(_(" --btmp-file <path> set an alternate path for btmp\n"), out);
fputs(USAGE_SEPARATOR, out);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
-
- fprintf(out, _("\nAvailable columns:\n"));
+ printf(USAGE_HELP_OPTIONS(26));
+ fputs(USAGE_COLUMNS, out);
for (i = 0; i < ARRAY_SIZE(coldescs); i++)
- fprintf(out, " %14s %s\n", coldescs[i].name,
- _(coldescs[i].help));
+ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help));
- fprintf(out, _("\nFor more details see lslogins(1).\n"));
+ printf(USAGE_MAN_TAIL("lslogins(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
- int c, opt_o = 0;
- char *logins = NULL, *groups = NULL;
+ int c;
+ char *logins = NULL, *groups = NULL, *outarg = NULL;
char *path_wtmp = _PATH_WTMP, *path_btmp = _PATH_BTMP;
struct lslogins_control *ctl = xcalloc(1, sizeof(struct lslogins_control));
size_t i;
/* long only options. */
enum {
- OPT_VER = CHAR_MAX + 1,
- OPT_WTMP,
+ OPT_WTMP = CHAR_MAX + 1,
OPT_BTMP,
OPT_NOTRUNC,
OPT_NOHEAD,
OPT_TIME_FMT,
+ OPT_OUTPUT_ALL,
};
static const struct option longopts[] = {
{ "notruncate", no_argument, 0, OPT_NOTRUNC },
{ "noheadings", no_argument, 0, OPT_NOHEAD },
{ "output", required_argument, 0, 'o' },
+ { "output-all", no_argument, 0, OPT_OUTPUT_ALL },
{ "last", no_argument, 0, 'L', },
{ "raw", no_argument, 0, 'r' },
{ "system-accs", no_argument, 0, 's' },
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
ctl->time_mode = TIME_SHORT;
add_column(columns, ncolumns++, COL_UID);
add_column(columns, ncolumns++, COL_USER);
- while ((c = getopt_long(argc, argv, "acfGg:hLl:no:prsuVxzZ",
+ while ((c = getopt_long(argc, argv, "acefGg:hLl:no:prsuVzZ",
longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
groups = optarg;
break;
case 'h':
- usage(stdout);
+ usage();
break;
case 'L':
add_column(columns, ncolumns++, COL_LAST_TTY);
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;
- }
- opt_o = 1;
+ if (*optarg == '=')
+ optarg++;
+ outarg = optarg;
+ break;
+ case OPT_OUTPUT_ALL:
+ for (ncolumns = 0; ncolumns < ARRAY_SIZE(coldescs); ncolumns++)
+ columns[ncolumns] = ncolumns;
break;
case 'r':
outmode = OUT_RAW;
add_column(columns, ncolumns++, COL_PWDDENY);
add_column(columns, ncolumns++, COL_NOLOGIN);
add_column(columns, ncolumns++, COL_HUSH_STATUS);
+ add_column(columns, ncolumns++, COL_PWDMETHOD);
break;
case 'z':
outmode = OUT_NUL;
ctl->noheadings = 1;
break;
case OPT_TIME_FMT:
- {
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
- if (strcmp(timefmts[i].name, optarg) == 0) {
- ctl->time_mode = timefmts[i].val;
- break;
- }
- }
- if (ctl->time_mode == TIME_INVALID)
- usage(stderr);
- }
+ ctl->time_mode = parse_time_mode(optarg);
break;
case 'V':
- printf(UTIL_LINUX_VERSION);
- return EXIT_SUCCESS;
+ print_version(EXIT_SUCCESS);
case 'Z':
{
#ifdef HAVE_LIBSELINUX
break;
}
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users."));
logins = argv[optind];
outmode = OUT_PRETTY;
+ ctl->fail_on_unknown = 1;
} else if (argc != optind)
- usage(stderr);
+ errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users."));
scols_init_debug(0);
if (lslogins_flag & F_USRAC && lslogins_flag & F_SYSAC)
lslogins_flag &= ~(F_USRAC | F_SYSAC);
- if (outmode == OUT_PRETTY && !opt_o) {
+ if (outmode == OUT_PRETTY) {
/* all columns for lslogins <username> */
for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++)
columns[ncolumns++] = i;
- } else if (ncolumns == 2 && !opt_o) {
- /* default colummns */
+ } else if (ncolumns == 2) {
+ /* default columns */
add_column(columns, ncolumns++, COL_NPROCS);
add_column(columns, ncolumns++, COL_PWDLOCK);
add_column(columns, ncolumns++, COL_PWDDENY);
add_column(columns, ncolumns++, COL_GECOS);
}
+ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
if (require_wtmp())
parse_wtmp(ctl, path_wtmp);
if (require_btmp())
parse_btmp(ctl, path_btmp);
- get_ulist(ctl, logins, groups);
+ if (logins || groups)
+ get_ulist(ctl, logins, groups);
if (create_usertree(ctl))
return EXIT_FAILURE;