#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
};
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;
#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_8601_DATE|ISO_8601_TIME|ISO_8601_TIMEZONE,
+ buf, sizeof(buf));
break;
case TIME_ISO_SHORT:
- strftime(buf, sizeof(buf), "%Y-%m-%d", &tm);
+ rc = strtime_iso(&time, ISO_8601_DATE, buf, sizeof(buf));
break;
default:
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;
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;
}
const char *p;
for (p = str; p && *p; p++)
- if (!isalnum((unsigned int) *p))
+ if (!isalnum((unsigned char) *p))
return 0;
return p > str ? 1 : 0;
}
struct passwd *pwd;
struct group *grp;
struct spwd *shadow;
- struct utmp *user_wtmp = NULL, *user_btmp = NULL;
+ struct utmpx *user_wtmp = NULL, *user_btmp = NULL;
int n = 0;
time_t time;
uid_t uid;
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;
}
if (groups) {
- /* FIXME: this might lead to duplicit entries, although not visible
+ /* 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;
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)
- if (IS_REAL_ERRNO(errno))
- return -1;
+ if (!*user && IS_REAL_ERRNO(errno))
+ return -1;
return 0;
}
static struct libscols_table *setup_table(struct lslogins_control *ctl)
{
- struct libscols_table *tb = scols_new_table();
+ struct libscols_table *table = scols_new_table();
int 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;
}
return;
ln = scols_table_new_line(tb, NULL);
+ if (!ln)
+ err(EXIT_FAILURE, _("failed to allocate output line"));
+
while (n < ncolumns) {
int rc = 0;
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;
}
#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);
free(u);
}
-static int parse_time_mode(const char *optarg)
+static int parse_time_mode(const char *s)
{
struct lslogins_timefmt {
const char *name;
size_t i;
for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
- if (strcmp(timefmts[i].name, optarg) == 0)
+ if (strcmp(timefmts[i].name, s) == 0)
return timefmts[i].val;
}
- errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
+ errx(EXIT_FAILURE, _("unknown time format: %s"), s);
}
static void __attribute__((__noreturn__)) usage(FILE *out)
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options]\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);
break;
}
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}