]>
Commit | Line | Data |
---|---|---|
ab1cfad5 OO |
1 | /* |
2 | * lslogins - List information about users on the system | |
3 | * | |
4 | * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> | |
8b5e2279 | 5 | * Copyright (C) 2014 Karel Zak <kzak@redhat.com> |
ab1cfad5 OO |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | */ | |
21 | ||
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <unistd.h> | |
25 | #include <getopt.h> | |
26 | #include <sys/types.h> | |
29cc2a55 OO |
27 | #include <sys/stat.h> |
28 | #include <sys/syslog.h> | |
ab1cfad5 | 29 | #include <pwd.h> |
29cc2a55 | 30 | #include <grp.h> |
ab1cfad5 OO |
31 | #include <shadow.h> |
32 | #include <paths.h> | |
33 | #include <time.h> | |
29cc2a55 OO |
34 | #include <utmp.h> |
35 | #include <signal.h> | |
36 | #include <err.h> | |
37 | #include <limits.h> | |
dd7760a8 OO |
38 | #include <search.h> |
39 | ||
29cc2a55 OO |
40 | #include <libsmartcols.h> |
41 | #ifdef HAVE_LIBSELINUX | |
8b5e2279 | 42 | # include <selinux/selinux.h> |
29cc2a55 | 43 | #endif |
ab1cfad5 | 44 | |
f37b357b KZ |
45 | #ifdef HAVE_LIBSYSTEMD |
46 | # include <systemd/sd-journal.h> | |
47 | #endif | |
48 | ||
ab1cfad5 OO |
49 | #include "c.h" |
50 | #include "nls.h" | |
51 | #include "closestream.h" | |
52 | #include "xalloc.h" | |
29cc2a55 | 53 | #include "list.h" |
ab1cfad5 OO |
54 | #include "strutils.h" |
55 | #include "optutils.h" | |
29cc2a55 OO |
56 | #include "pathnames.h" |
57 | #include "logindefs.h" | |
d3a93df8 | 58 | #include "procutils.h" |
88134ee2 | 59 | #include "timeutils.h" |
ab1cfad5 OO |
60 | |
61 | /* | |
62 | * column description | |
63 | */ | |
64 | struct lslogins_coldesc { | |
65 | const char *name; | |
66 | const char *help; | |
c77108b8 | 67 | const char *pretty_name; |
ab1cfad5 | 68 | |
29cc2a55 | 69 | double whint; /* width hint */ |
c77108b8 | 70 | long flag; |
ab1cfad5 OO |
71 | }; |
72 | ||
c6cf4d1c | 73 | static int lslogins_flag; |
ab1cfad5 | 74 | |
c77108b8 OO |
75 | #define UL_UID_MIN 1000 |
76 | #define UL_UID_MAX 60000 | |
77 | #define UL_SYS_UID_MIN 201 | |
78 | #define UL_SYS_UID_MAX 999 | |
29cc2a55 | 79 | |
ab1cfad5 OO |
80 | /* we use the value of outmode to determine |
81 | * appropriate flags for the libsmartcols table | |
82 | * (e.g., a value of out_newline would imply a raw | |
83 | * table with the column separator set to '\n'). | |
84 | */ | |
85 | static int outmode; | |
86 | /* | |
87 | * output modes | |
88 | */ | |
89 | enum { | |
10c74524 KZ |
90 | OUT_COLON = 1, |
91 | OUT_EXPORT, | |
92 | OUT_NEWLINE, | |
93 | OUT_RAW, | |
94 | OUT_NUL, | |
95 | OUT_PRETTY | |
ab1cfad5 OO |
96 | }; |
97 | ||
98 | struct lslogins_user { | |
99 | char *login; | |
100 | uid_t uid; | |
101 | char *group; | |
102 | gid_t gid; | |
103 | char *gecos; | |
104 | ||
f47c6d3a | 105 | int pwd_empty; |
29cc2a55 | 106 | int nologin; |
f47c6d3a KZ |
107 | int pwd_lock; |
108 | int pwd_deny; | |
ab1cfad5 | 109 | |
68657ea2 KZ |
110 | gid_t *sgroups; |
111 | size_t nsgroups; | |
ab1cfad5 | 112 | |
29cc2a55 | 113 | char *pwd_ctime; |
c6cf4d1c | 114 | char *pwd_warn; |
38496592 | 115 | char *pwd_expire; |
29cc2a55 OO |
116 | char *pwd_ctime_min; |
117 | char *pwd_ctime_max; | |
ab1cfad5 | 118 | |
29cc2a55 OO |
119 | char *last_login; |
120 | char *last_tty; | |
121 | char *last_hostname; | |
ab1cfad5 | 122 | |
29cc2a55 OO |
123 | char *failed_login; |
124 | char *failed_tty; | |
ab1cfad5 | 125 | |
29cc2a55 OO |
126 | #ifdef HAVE_LIBSELINUX |
127 | security_context_t context; | |
128 | #endif | |
ab1cfad5 OO |
129 | char *homedir; |
130 | char *shell; | |
131 | char *pwd_status; | |
29cc2a55 | 132 | int hushed; |
d3a93df8 | 133 | char *nprocs; |
29cc2a55 | 134 | |
ab1cfad5 | 135 | }; |
38496592 OO |
136 | |
137 | /* | |
138 | * time modes | |
139 | * */ | |
140 | enum { | |
c20ffb8f | 141 | TIME_INVALID = 0, |
38496592 OO |
142 | TIME_SHORT, |
143 | TIME_FULL, | |
4ea7848b | 144 | TIME_ISO, |
aa45b344 | 145 | TIME_ISO_SHORT, |
38496592 OO |
146 | }; |
147 | ||
ab1cfad5 OO |
148 | /* |
149 | * flags | |
150 | */ | |
151 | enum { | |
29cc2a55 OO |
152 | F_SYSAC = (1 << 3), |
153 | F_USRAC = (1 << 4), | |
ab1cfad5 OO |
154 | }; |
155 | ||
156 | /* | |
157 | * IDs | |
158 | */ | |
159 | enum { | |
f47c6d3a | 160 | COL_USER = 0, |
ab1cfad5 | 161 | COL_UID, |
10c74524 KZ |
162 | COL_GECOS, |
163 | COL_HOME, | |
164 | COL_SHELL, | |
f47c6d3a KZ |
165 | COL_NOLOGIN, |
166 | COL_PWDLOCK, | |
167 | COL_PWDEMPTY, | |
168 | COL_PWDDENY, | |
8b13a4d8 KZ |
169 | COL_GROUP, |
170 | COL_GID, | |
171 | COL_SGROUPS, | |
68657ea2 | 172 | COL_SGIDS, |
ab1cfad5 OO |
173 | COL_LAST_LOGIN, |
174 | COL_LAST_TTY, | |
175 | COL_LAST_HOSTNAME, | |
176 | COL_FAILED_LOGIN, | |
177 | COL_FAILED_TTY, | |
178 | COL_HUSH_STATUS, | |
c6cf4d1c | 179 | COL_PWD_WARN, |
ab1cfad5 | 180 | COL_PWD_CTIME, |
29cc2a55 OO |
181 | COL_PWD_CTIME_MIN, |
182 | COL_PWD_CTIME_MAX, | |
38496592 | 183 | COL_PWD_EXPIR, |
29cc2a55 | 184 | COL_SELINUX, |
d3a93df8 | 185 | COL_NPROCS, |
ab1cfad5 OO |
186 | }; |
187 | ||
ed374569 KZ |
188 | #define is_wtmp_col(x) ((x) == COL_LAST_LOGIN || \ |
189 | (x) == COL_LAST_TTY || \ | |
190 | (x) == COL_LAST_HOSTNAME) | |
191 | ||
192 | #define is_btmp_col(x) ((x) == COL_FAILED_LOGIN || \ | |
193 | (x) == COL_FAILED_TTY) | |
194 | ||
a94baf36 KZ |
195 | enum { |
196 | STATUS_FALSE = 0, | |
197 | STATUS_TRUE, | |
198 | STATUS_UNKNOWN | |
199 | }; | |
200 | ||
201 | static const char *const status[] = { | |
202 | [STATUS_FALSE] = "0", | |
203 | [STATUS_TRUE] = "1", | |
204 | [STATUS_UNKNOWN]= NULL | |
205 | }; | |
206 | ||
10c74524 KZ |
207 | static const char *const pretty_status[] = { |
208 | [STATUS_FALSE] = N_("no"), | |
209 | [STATUS_TRUE] = N_("yes"), | |
210 | [STATUS_UNKNOWN]= NULL | |
211 | }; | |
212 | ||
213 | #define get_status(x) (outmode == OUT_PRETTY ? pretty_status[(x)] : status[(x)]) | |
214 | ||
bdba3fd9 | 215 | static const struct lslogins_coldesc coldescs[] = |
ab1cfad5 | 216 | { |
ea24eacc KZ |
217 | [COL_USER] = { "USER", N_("user name"), N_("Username"), 0.1, SCOLS_FL_NOEXTREMES }, |
218 | [COL_UID] = { "UID", N_("user ID"), "UID", 1, SCOLS_FL_RIGHT}, | |
b8ec0ab7 | 219 | [COL_PWDEMPTY] = { "PWD-EMPTY", N_("password not required"), N_("Password not required"), 1, SCOLS_FL_RIGHT }, |
f47c6d3a KZ |
220 | [COL_PWDDENY] = { "PWD-DENY", N_("login by password disabled"), N_("Login by password disabled"), 1, SCOLS_FL_RIGHT }, |
221 | [COL_PWDLOCK] = { "PWD-LOCK", N_("password defined, but locked"), N_("Password is locked"), 1, SCOLS_FL_RIGHT }, | |
222 | [COL_NOLOGIN] = { "NOLOGIN", N_("log in disabled by nologin(8) or pam_nologin(8)"), N_("No login"), 1, SCOLS_FL_RIGHT }, | |
b8ec0ab7 BS |
223 | [COL_GROUP] = { "GROUP", N_("primary group name"), N_("Primary group"), 0.1 }, |
224 | [COL_GID] = { "GID", N_("primary group ID"), "GID", 1, SCOLS_FL_RIGHT }, | |
225 | [COL_SGROUPS] = { "SUPP-GROUPS", N_("supplementary group names"), N_("Supplementary groups"), 0.1 }, | |
3e5cba73 | 226 | [COL_SGIDS] = { "SUPP-GIDS", N_("supplementary group IDs"), N_("Supplementary group IDs"), 0.1 }, |
ea24eacc | 227 | [COL_HOME] = { "HOMEDIR", N_("home directory"), N_("Home directory"), 0.1 }, |
3e5cba73 | 228 | [COL_SHELL] = { "SHELL", N_("login shell"), N_("Shell"), 0.1 }, |
ea24eacc KZ |
229 | [COL_GECOS] = { "GECOS", N_("full user name"), N_("Gecos field"), 0.1, SCOLS_FL_TRUNC }, |
230 | [COL_LAST_LOGIN] = { "LAST-LOGIN", N_("date of last login"), N_("Last login"), 0.1, SCOLS_FL_RIGHT }, | |
3e5cba73 | 231 | [COL_LAST_TTY] = { "LAST-TTY", N_("last tty used"), N_("Last terminal"), 0.05 }, |
ea24eacc KZ |
232 | [COL_LAST_HOSTNAME] = { "LAST-HOSTNAME",N_("hostname during the last session"), N_("Last hostname"), 0.1}, |
233 | [COL_FAILED_LOGIN] = { "FAILED-LOGIN", N_("date of last failed login"), N_("Failed login"), 0.1 }, | |
3e5cba73 | 234 | [COL_FAILED_TTY] = { "FAILED-TTY", N_("where did the login fail?"), N_("Failed login terminal"), 0.05 }, |
74fddd86 | 235 | [COL_HUSH_STATUS] = { "HUSHED", N_("user's hush settings"), N_("Hushed"), 1, SCOLS_FL_RIGHT }, |
ed374569 KZ |
236 | [COL_PWD_WARN] = { "PWD-WARN", N_("days user is warned of password expiration"), N_("Password expiration warn interval"), 0.1, SCOLS_FL_RIGHT }, |
237 | [COL_PWD_EXPIR] = { "PWD-EXPIR", N_("password expiration date"), N_("Password expiration"), 0.1, SCOLS_FL_RIGHT }, | |
238 | [COL_PWD_CTIME] = { "PWD-CHANGE", N_("date of last password change"), N_("Password changed"), 0.1, SCOLS_FL_RIGHT}, | |
b8ec0ab7 BS |
239 | [COL_PWD_CTIME_MIN] = { "PWD-MIN", N_("number of days required between changes"), N_("Minimum change time"), 0.1, SCOLS_FL_RIGHT }, |
240 | [COL_PWD_CTIME_MAX] = { "PWD-MAX", N_("max number of days a password may remain unchanged"), N_("Maximum change time"), 0.1, SCOLS_FL_RIGHT }, | |
ea24eacc | 241 | [COL_SELINUX] = { "CONTEXT", N_("the user's security context"), N_("Selinux context"), 0.1 }, |
b8ec0ab7 | 242 | [COL_NPROCS] = { "PROC", N_("number of processes run by the user"), N_("Running processes"), 1, SCOLS_FL_RIGHT }, |
ab1cfad5 OO |
243 | }; |
244 | ||
c6cf4d1c OO |
245 | struct lslogins_control { |
246 | struct utmp *wtmp; | |
247 | size_t wtmp_size; | |
248 | ||
249 | struct utmp *btmp; | |
250 | size_t btmp_size; | |
251 | ||
252 | void *usertree; | |
253 | ||
f37b357b | 254 | uid_t uid; |
c6cf4d1c OO |
255 | uid_t UID_MIN; |
256 | uid_t UID_MAX; | |
257 | ||
258 | uid_t SYS_UID_MIN; | |
259 | uid_t SYS_UID_MAX; | |
260 | ||
c6cf4d1c OO |
261 | char **ulist; |
262 | size_t ulsiz; | |
c77108b8 | 263 | |
38496592 | 264 | unsigned int time_mode; |
d3a93df8 | 265 | |
f37b357b | 266 | const char *journal_path; |
37019cbc | 267 | |
4b6ae071 | 268 | unsigned int selinux_enabled : 1, |
0d75c73d | 269 | ulist_on : 1, |
bdba3fd9 KZ |
270 | noheadings : 1, |
271 | notrunc : 1; | |
c6cf4d1c | 272 | }; |
f37b357b | 273 | |
1624245f KZ |
274 | /* these have to remain global since there's no other reasonable way to pass |
275 | * them for each call of fill_table() via twalk() */ | |
c6cf4d1c | 276 | static struct libscols_table *tb; |
87a144a5 KZ |
277 | |
278 | /* columns[] array specifies all currently wanted output column. The columns | |
279 | * are defined by coldescs[] array and you can specify (on command line) each | |
280 | * column twice. That's enough, dynamically allocated array of the columns is | |
281 | * unnecessary overkill and over-engineering in this case */ | |
282 | static int columns[ARRAY_SIZE(coldescs) * 2]; | |
c6cf4d1c | 283 | static int ncolumns; |
dd7760a8 | 284 | |
87a144a5 KZ |
285 | static inline size_t err_columns_index(size_t arysz, size_t idx) |
286 | { | |
287 | if (idx >= arysz) | |
288 | errx(EXIT_FAILURE, _("too many columns specified, " | |
1d231190 | 289 | "the limit is %zu columns"), |
87a144a5 KZ |
290 | arysz - 1); |
291 | return idx; | |
292 | } | |
293 | ||
294 | #define add_column(ary, n, id) \ | |
295 | ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) | |
296 | ||
1624245f | 297 | static int column_name_to_id(const char *name, size_t namesz) |
ab1cfad5 OO |
298 | { |
299 | size_t i; | |
300 | ||
301 | for (i = 0; i < ARRAY_SIZE(coldescs); i++) { | |
302 | const char *cn = coldescs[i].name; | |
303 | ||
1624245f | 304 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) |
ab1cfad5 OO |
305 | return i; |
306 | } | |
307 | warnx(_("unknown column: %s"), name); | |
308 | return -1; | |
309 | } | |
ed374569 | 310 | |
cb5b5288 KZ |
311 | static struct timeval now; |
312 | ||
38496592 | 313 | static char *make_time(int mode, time_t time) |
29cc2a55 | 314 | { |
cb5b5288 | 315 | int rc = 0; |
ea24eacc | 316 | char buf[64] = {0}; |
29cc2a55 | 317 | |
38496592 | 318 | switch(mode) { |
1624245f | 319 | case TIME_FULL: |
cb5b5288 KZ |
320 | { |
321 | char *s; | |
322 | struct tm tm; | |
323 | localtime_r(&time, &tm); | |
324 | ||
1624245f KZ |
325 | asctime_r(&tm, buf); |
326 | if (*(s = buf + strlen(buf) - 1) == '\n') | |
327 | *s = '\0'; | |
cb5b5288 | 328 | rc = 0; |
1624245f | 329 | break; |
cb5b5288 | 330 | } |
1624245f | 331 | case TIME_SHORT: |
cb5b5288 KZ |
332 | rc = strtime_short(&time, &now, UL_SHORTTIME_THISYEAR_HHMM, |
333 | buf, sizeof(buf)); | |
1624245f KZ |
334 | break; |
335 | case TIME_ISO: | |
cb5b5288 KZ |
336 | rc = strtime_iso(&time, ISO_8601_DATE|ISO_8601_TIME|ISO_8601_TIMEZONE, |
337 | buf, sizeof(buf)); | |
1624245f | 338 | break; |
aa45b344 | 339 | case TIME_ISO_SHORT: |
cb5b5288 | 340 | rc = strtime_iso(&time, ISO_8601_DATE, buf, sizeof(buf)); |
aa45b344 | 341 | break; |
1624245f | 342 | default: |
b5af3ee8 | 343 | errx(EXIT_FAILURE, _("unsupported time type")); |
38496592 | 344 | } |
cb5b5288 KZ |
345 | |
346 | if (rc) | |
347 | errx(EXIT_FAILURE, _("failed to compose time string")); | |
348 | ||
38496592 | 349 | return xstrdup(buf); |
29cc2a55 | 350 | } |
c6cf4d1c | 351 | |
29cc2a55 OO |
352 | |
353 | static char *uidtostr(uid_t uid) | |
354 | { | |
355 | char *str_uid = NULL; | |
5424d2a2 KZ |
356 | xasprintf(&str_uid, "%u", uid); |
357 | return str_uid; | |
29cc2a55 OO |
358 | } |
359 | ||
360 | static char *gidtostr(gid_t gid) | |
361 | { | |
362 | char *str_gid = NULL; | |
5424d2a2 KZ |
363 | xasprintf(&str_gid, "%u", gid); |
364 | return str_gid; | |
29cc2a55 OO |
365 | } |
366 | ||
68657ea2 | 367 | static char *build_sgroups_string(gid_t *sgroups, size_t nsgroups, int want_names) |
29cc2a55 | 368 | { |
68657ea2 KZ |
369 | size_t n = 0, maxlen, len; |
370 | char *res, *p; | |
371 | ||
f37b357b KZ |
372 | if (!nsgroups) |
373 | return NULL; | |
374 | ||
68657ea2 KZ |
375 | len = maxlen = nsgroups * 10; |
376 | res = p = xmalloc(maxlen); | |
377 | ||
378 | while (n < nsgroups) { | |
379 | int x; | |
380 | again: | |
381 | if (!want_names) | |
382 | x = snprintf(p, len, "%u,", sgroups[n]); | |
383 | else { | |
384 | struct group *grp = getgrgid(sgroups[n]); | |
385 | if (!grp) { | |
386 | free(res); | |
387 | return NULL; | |
388 | } | |
389 | x = snprintf(p, len, "%s,", grp->gr_name); | |
c6cf4d1c | 390 | } |
29cc2a55 | 391 | |
06fa5817 | 392 | if (x < 0 || (size_t) x >= len) { |
68657ea2 | 393 | size_t cur = p - res; |
29cc2a55 | 394 | |
68657ea2 KZ |
395 | maxlen *= 2; |
396 | res = xrealloc(res, maxlen); | |
397 | p = res + cur; | |
398 | len = maxlen - cur; | |
399 | goto again; | |
400 | } | |
29cc2a55 | 401 | |
68657ea2 KZ |
402 | len -= x; |
403 | p += x; | |
29cc2a55 | 404 | ++n; |
29cc2a55 | 405 | } |
c6cf4d1c | 406 | |
68657ea2 KZ |
407 | if (p > res) |
408 | *(p - 1) = '\0'; | |
29cc2a55 | 409 | |
68657ea2 | 410 | return res; |
29cc2a55 | 411 | } |
c6cf4d1c | 412 | |
c6cf4d1c | 413 | static struct utmp *get_last_wtmp(struct lslogins_control *ctl, const char *username) |
29cc2a55 OO |
414 | { |
415 | size_t n = 0; | |
416 | size_t len; | |
417 | ||
418 | if (!username) | |
419 | return NULL; | |
420 | ||
421 | len = strlen(username); | |
c6cf4d1c OO |
422 | n = ctl->wtmp_size - 1; |
423 | do { | |
424 | if (!strncmp(username, ctl->wtmp[n].ut_user, | |
425 | len < UT_NAMESIZE ? len : UT_NAMESIZE)) | |
426 | return ctl->wtmp + n; | |
427 | } while (n--); | |
29cc2a55 OO |
428 | return NULL; |
429 | ||
430 | } | |
8b5e2279 | 431 | |
ed374569 KZ |
432 | static int require_wtmp(void) |
433 | { | |
434 | size_t i; | |
435 | for (i = 0; i < (size_t) ncolumns; i++) | |
436 | if (is_wtmp_col(columns[i])) | |
437 | return 1; | |
438 | return 0; | |
439 | } | |
440 | ||
441 | static int require_btmp(void) | |
442 | { | |
443 | size_t i; | |
444 | for (i = 0; i < (size_t) ncolumns; i++) | |
445 | if (is_btmp_col(columns[i])) | |
446 | return 1; | |
447 | return 0; | |
448 | } | |
449 | ||
c6cf4d1c | 450 | static struct utmp *get_last_btmp(struct lslogins_control *ctl, const char *username) |
29cc2a55 OO |
451 | { |
452 | size_t n = 0; | |
453 | size_t len; | |
454 | ||
455 | if (!username) | |
456 | return NULL; | |
457 | ||
458 | len = strlen(username); | |
c6cf4d1c OO |
459 | n = ctl->btmp_size - 1; |
460 | do { | |
461 | if (!strncmp(username, ctl->btmp[n].ut_user, | |
462 | len < UT_NAMESIZE ? len : UT_NAMESIZE)) | |
463 | return ctl->btmp + n; | |
464 | }while (n--); | |
29cc2a55 OO |
465 | return NULL; |
466 | ||
467 | } | |
468 | ||
a6bf40ee KZ |
469 | static int read_utmp(char const *file, size_t *nents, struct utmp **res) |
470 | { | |
471 | size_t n_read = 0, n_alloc = 0; | |
472 | struct utmp *utmp = NULL, *u; | |
473 | ||
474 | if (utmpname(file) < 0) | |
475 | return -errno; | |
476 | ||
477 | setutent(); | |
478 | errno = 0; | |
479 | ||
480 | while ((u = getutent()) != NULL) { | |
481 | if (n_read == n_alloc) { | |
482 | n_alloc += 32; | |
483 | utmp = xrealloc(utmp, n_alloc * sizeof (struct utmp)); | |
484 | } | |
485 | utmp[n_read++] = *u; | |
486 | } | |
487 | if (!u && errno) { | |
488 | free(utmp); | |
489 | return -errno; | |
490 | } | |
491 | ||
492 | endutent(); | |
493 | ||
494 | *nents = n_read; | |
495 | *res = utmp; | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
c6cf4d1c | 500 | static int parse_wtmp(struct lslogins_control *ctl, char *path) |
29cc2a55 OO |
501 | { |
502 | int rc = 0; | |
503 | ||
c6cf4d1c | 504 | rc = read_utmp(path, &ctl->wtmp_size, &ctl->wtmp); |
29cc2a55 | 505 | if (rc < 0 && errno != EACCES) |
c6cf4d1c | 506 | err(EXIT_FAILURE, "%s", path); |
29cc2a55 OO |
507 | return rc; |
508 | } | |
29cc2a55 | 509 | |
c6cf4d1c OO |
510 | static int parse_btmp(struct lslogins_control *ctl, char *path) |
511 | { | |
512 | int rc = 0; | |
513 | ||
514 | rc = read_utmp(path, &ctl->btmp_size, &ctl->btmp); | |
515 | if (rc < 0 && errno != EACCES) | |
516 | err(EXIT_FAILURE, "%s", path); | |
517 | return rc; | |
518 | } | |
8b5e2279 | 519 | |
68657ea2 | 520 | static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd) |
29cc2a55 | 521 | { |
68657ea2 | 522 | size_t n = 0; |
8b7ef916 | 523 | int ngroups = 0; |
29cc2a55 | 524 | |
c6cf4d1c OO |
525 | *len = 0; |
526 | *list = NULL; | |
527 | ||
528 | /* first let's get a supp. group count */ | |
8b7ef916 KZ |
529 | getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups); |
530 | if (!ngroups) | |
29cc2a55 OO |
531 | return -1; |
532 | ||
8b7ef916 | 533 | *list = xcalloc(1, ngroups * sizeof(gid_t)); |
29cc2a55 | 534 | |
c6cf4d1c | 535 | /* now for the actual list of GIDs */ |
8b7ef916 | 536 | if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups)) |
29cc2a55 OO |
537 | return -1; |
538 | ||
8b7ef916 KZ |
539 | *len = (size_t) ngroups; |
540 | ||
c6cf4d1c | 541 | /* getgroups also returns the user's primary GID - dispose of it */ |
29cc2a55 OO |
542 | while (n < *len) { |
543 | if ((*list)[n] == pwd->pw_gid) | |
544 | break; | |
545 | ++n; | |
546 | } | |
29cc2a55 | 547 | |
f37b357b KZ |
548 | if (*len) |
549 | (*list)[n] = (*list)[--(*len)]; | |
8b7ef916 | 550 | |
29cc2a55 | 551 | return 0; |
29cc2a55 OO |
552 | } |
553 | ||
d3a93df8 OO |
554 | static int get_nprocs(const uid_t uid) |
555 | { | |
556 | int nprocs = 0; | |
557 | pid_t pid; | |
558 | struct proc_processes *proc = proc_open_processes(); | |
559 | ||
560 | proc_processes_filter_by_uid(proc, uid); | |
561 | ||
562 | while (!proc_next_pid(proc, &pid)) | |
563 | ++nprocs; | |
564 | ||
565 | proc_close_processes(proc); | |
566 | return nprocs; | |
567 | } | |
568 | ||
f47c6d3a KZ |
569 | static int valid_pwd(const char *str) |
570 | { | |
571 | const char *p; | |
572 | ||
573 | for (p = str; p && *p; p++) | |
574 | if (!isalnum((unsigned int) *p)) | |
575 | return 0; | |
576 | return p > str ? 1 : 0; | |
577 | } | |
578 | ||
c6cf4d1c | 579 | static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const char *username) |
29cc2a55 OO |
580 | { |
581 | struct lslogins_user *user; | |
c6cf4d1c OO |
582 | struct passwd *pwd; |
583 | struct group *grp; | |
584 | struct spwd *shadow; | |
29cc2a55 OO |
585 | struct utmp *user_wtmp = NULL, *user_btmp = NULL; |
586 | int n = 0; | |
587 | time_t time; | |
29cc2a55 | 588 | uid_t uid; |
c6cf4d1c | 589 | errno = 0; |
1ebfc60a | 590 | |
1624245f | 591 | pwd = username ? getpwnam(username) : getpwent(); |
29cc2a55 OO |
592 | if (!pwd) |
593 | return NULL; | |
594 | ||
f37b357b KZ |
595 | ctl->uid = uid = pwd->pw_uid; |
596 | ||
1624245f KZ |
597 | /* nfsnobody is an exception to the UID_MAX limit. This is "nobody" on |
598 | * some systems; the decisive point is the UID - 65534 */ | |
8b13a4d8 KZ |
599 | if ((lslogins_flag & F_USRAC) && |
600 | strcmp("nfsnobody", pwd->pw_name) != 0 && | |
601 | uid != 0) { | |
c6cf4d1c | 602 | if (uid < ctl->UID_MIN || uid > ctl->UID_MAX) { |
29cc2a55 OO |
603 | errno = EAGAIN; |
604 | return NULL; | |
605 | } | |
8b5e2279 | 606 | |
1624245f | 607 | } else if ((lslogins_flag & F_SYSAC) && |
d8b2ddb7 | 608 | (uid < ctl->SYS_UID_MIN || uid > ctl->SYS_UID_MAX)) { |
1624245f KZ |
609 | errno = EAGAIN; |
610 | return NULL; | |
29cc2a55 | 611 | } |
c6cf4d1c OO |
612 | |
613 | user = xcalloc(1, sizeof(struct lslogins_user)); | |
614 | ||
615 | grp = getgrgid(pwd->pw_gid); | |
29cc2a55 OO |
616 | if (!grp) |
617 | return NULL; | |
618 | ||
c6cf4d1c OO |
619 | if (ctl->wtmp) |
620 | user_wtmp = get_last_wtmp(ctl, pwd->pw_name); | |
621 | if (ctl->btmp) | |
622 | user_btmp = get_last_btmp(ctl, pwd->pw_name); | |
29cc2a55 | 623 | |
29cc2a55 | 624 | lckpwdf(); |
c6cf4d1c | 625 | shadow = getspnam(pwd->pw_name); |
29cc2a55 | 626 | ulckpwdf(); |
29cc2a55 | 627 | |
d8b2ddb7 KZ |
628 | /* required by tseach() stuff */ |
629 | user->uid = pwd->pw_uid; | |
630 | ||
29cc2a55 OO |
631 | while (n < ncolumns) { |
632 | switch (columns[n++]) { | |
f47c6d3a | 633 | case COL_USER: |
8b5e2279 KZ |
634 | user->login = xstrdup(pwd->pw_name); |
635 | break; | |
636 | case COL_UID: | |
637 | user->uid = pwd->pw_uid; | |
638 | break; | |
8b13a4d8 | 639 | case COL_GROUP: |
8b5e2279 KZ |
640 | user->group = xstrdup(grp->gr_name); |
641 | break; | |
8b13a4d8 | 642 | case COL_GID: |
8b5e2279 KZ |
643 | user->gid = pwd->pw_gid; |
644 | break; | |
8b13a4d8 | 645 | case COL_SGROUPS: |
8b5e2279 KZ |
646 | case COL_SGIDS: |
647 | if (get_sgroups(&user->sgroups, &user->nsgroups, pwd)) | |
648 | err(EXIT_FAILURE, _("failed to get supplementary groups")); | |
649 | break; | |
650 | case COL_HOME: | |
651 | user->homedir = xstrdup(pwd->pw_dir); | |
652 | break; | |
653 | case COL_SHELL: | |
654 | user->shell = xstrdup(pwd->pw_shell); | |
655 | break; | |
656 | case COL_GECOS: | |
657 | user->gecos = xstrdup(pwd->pw_gecos); | |
658 | break; | |
659 | case COL_LAST_LOGIN: | |
660 | if (user_wtmp) { | |
661 | time = user_wtmp->ut_tv.tv_sec; | |
662 | user->last_login = make_time(ctl->time_mode, time); | |
663 | } | |
664 | break; | |
665 | case COL_LAST_TTY: | |
666 | if (user_wtmp) | |
667 | user->last_tty = xstrdup(user_wtmp->ut_line); | |
668 | break; | |
669 | case COL_LAST_HOSTNAME: | |
670 | if (user_wtmp) | |
671 | user->last_hostname = xstrdup(user_wtmp->ut_host); | |
672 | break; | |
673 | case COL_FAILED_LOGIN: | |
674 | if (user_btmp) { | |
675 | time = user_btmp->ut_tv.tv_sec; | |
676 | user->failed_login = make_time(ctl->time_mode, time); | |
677 | } | |
678 | break; | |
679 | case COL_FAILED_TTY: | |
680 | if (user_btmp) | |
681 | user->failed_tty = xstrdup(user_btmp->ut_line); | |
682 | break; | |
683 | case COL_HUSH_STATUS: | |
684 | user->hushed = get_hushlogin_status(pwd, 0); | |
685 | if (user->hushed == -1) | |
686 | user->hushed = STATUS_UNKNOWN; | |
687 | break; | |
f47c6d3a | 688 | case COL_PWDEMPTY: |
8b5e2279 KZ |
689 | if (shadow) { |
690 | if (!*shadow->sp_pwdp) /* '\0' */ | |
f47c6d3a KZ |
691 | user->pwd_empty = STATUS_TRUE; |
692 | } else | |
693 | user->pwd_empty = STATUS_UNKNOWN; | |
694 | break; | |
695 | case COL_PWDDENY: | |
696 | if (shadow) { | |
697 | if ((*shadow->sp_pwdp == '!' || | |
698 | *shadow->sp_pwdp == '*') && | |
699 | !valid_pwd(shadow->sp_pwdp + 1)) | |
700 | user->pwd_deny = STATUS_TRUE; | |
8b5e2279 | 701 | } else |
f47c6d3a KZ |
702 | user->pwd_deny = STATUS_UNKNOWN; |
703 | break; | |
704 | ||
705 | case COL_PWDLOCK: | |
706 | if (shadow) { | |
707 | if (*shadow->sp_pwdp == '!' && valid_pwd(shadow->sp_pwdp + 1)) | |
708 | user->pwd_lock = STATUS_TRUE; | |
709 | } else | |
710 | user->pwd_lock = STATUS_UNKNOWN; | |
8b5e2279 KZ |
711 | break; |
712 | case COL_NOLOGIN: | |
1624245f | 713 | if (strstr(pwd->pw_shell, "nologin")) |
8b5e2279 | 714 | user->nologin = 1; |
f47c6d3a | 715 | else if (pwd->pw_uid) |
4ecc6dbb SK |
716 | user->nologin = access(_PATH_NOLOGIN, F_OK) == 0 || |
717 | access(_PATH_VAR_NOLOGIN, F_OK) == 0; | |
8b5e2279 KZ |
718 | break; |
719 | case COL_PWD_WARN: | |
720 | if (shadow && shadow->sp_warn >= 0) | |
721 | xasprintf(&user->pwd_warn, "%ld", shadow->sp_warn); | |
722 | break; | |
723 | case COL_PWD_EXPIR: | |
724 | if (shadow && shadow->sp_expire >= 0) | |
aa45b344 SK |
725 | user->pwd_expire = make_time(ctl->time_mode == TIME_ISO ? |
726 | TIME_ISO_SHORT : ctl->time_mode, | |
1624245f | 727 | shadow->sp_expire * 86400); |
8b5e2279 KZ |
728 | break; |
729 | case COL_PWD_CTIME: | |
1624245f KZ |
730 | /* sp_lstchg is specified in days, showing hours |
731 | * (especially in non-GMT timezones) would only serve | |
732 | * to confuse */ | |
8b5e2279 | 733 | if (shadow) |
aa45b344 SK |
734 | user->pwd_ctime = make_time(ctl->time_mode == TIME_ISO ? |
735 | TIME_ISO_SHORT : ctl->time_mode, | |
1624245f | 736 | shadow->sp_lstchg * 86400); |
8b5e2279 KZ |
737 | break; |
738 | case COL_PWD_CTIME_MIN: | |
ed374569 KZ |
739 | if (shadow && shadow->sp_min > 0) |
740 | xasprintf(&user->pwd_ctime_min, "%ld", shadow->sp_min); | |
8b5e2279 KZ |
741 | break; |
742 | case COL_PWD_CTIME_MAX: | |
ed374569 KZ |
743 | if (shadow && shadow->sp_max > 0) |
744 | xasprintf(&user->pwd_ctime_max, "%ld", shadow->sp_max); | |
8b5e2279 KZ |
745 | break; |
746 | case COL_SELINUX: | |
29cc2a55 | 747 | #ifdef HAVE_LIBSELINUX |
37019cbc | 748 | if (ctl->selinux_enabled) { |
8b5e2279 | 749 | /* typedefs and pointers are pure evil */ |
37019cbc KZ |
750 | security_context_t con = NULL; |
751 | if (getcon(&con) == 0) | |
752 | user->context = con; | |
753 | } | |
29cc2a55 | 754 | #endif |
d3a93df8 | 755 | break; |
d3a93df8 OO |
756 | case COL_NPROCS: |
757 | xasprintf(&user->nprocs, "%d", get_nprocs(pwd->pw_uid)); | |
8b5e2279 KZ |
758 | break; |
759 | default: | |
760 | /* something went very wrong here */ | |
761 | err(EXIT_FAILURE, "fatal: unknown error"); | |
d3a93df8 | 762 | break; |
29cc2a55 OO |
763 | } |
764 | } | |
765 | ||
766 | return user; | |
767 | } | |
d3a93df8 | 768 | |
29cc2a55 | 769 | /* some UNIX implementations set errno iff a passwd/grp/... |
c6cf4d1c | 770 | * entry was not found. The original UNIX logins(1) utility always |
29cc2a55 | 771 | * ignores invalid login/group names, so we're going to as well.*/ |
c6cf4d1c OO |
772 | #define IS_REAL_ERRNO(e) !((e) == ENOENT || (e) == ESRCH || \ |
773 | (e) == EBADF || (e) == EPERM || (e) == EAGAIN) | |
774 | ||
c6cf4d1c | 775 | /* get a definitive list of users we want info about... */ |
c77108b8 OO |
776 | |
777 | static int str_to_uint(char *s, unsigned int *ul) | |
778 | { | |
779 | char *end; | |
780 | if (!s || !*s) | |
781 | return -1; | |
782 | *ul = strtoul(s, &end, 0); | |
783 | if (!*end) | |
784 | return 0; | |
785 | return 1; | |
786 | } | |
8b5e2279 | 787 | |
c6cf4d1c OO |
788 | static int get_ulist(struct lslogins_control *ctl, char *logins, char *groups) |
789 | { | |
790 | char *u, *g; | |
791 | size_t i = 0, n = 0, *arsiz; | |
792 | struct group *grp; | |
c77108b8 | 793 | struct passwd *pwd; |
c6cf4d1c | 794 | char ***ar; |
c77108b8 OO |
795 | uid_t uid; |
796 | gid_t gid; | |
c6cf4d1c OO |
797 | |
798 | ar = &ctl->ulist; | |
799 | arsiz = &ctl->ulsiz; | |
800 | ||
801 | /* an arbitrary starting value */ | |
802 | *arsiz = 32; | |
803 | *ar = xcalloc(1, sizeof(char *) * (*arsiz)); | |
804 | ||
0d75c73d KZ |
805 | if (logins) { |
806 | while ((u = strtok(logins, ","))) { | |
807 | logins = NULL; | |
c6cf4d1c | 808 | |
0d75c73d KZ |
809 | /* user specified by UID? */ |
810 | if (!str_to_uint(u, &uid)) { | |
811 | pwd = getpwuid(uid); | |
812 | if (!pwd) | |
813 | continue; | |
814 | u = pwd->pw_name; | |
815 | } | |
816 | (*ar)[i++] = xstrdup(u); | |
c6cf4d1c | 817 | |
0d75c73d KZ |
818 | if (i == *arsiz) |
819 | *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32)); | |
820 | } | |
821 | ctl->ulist_on = 1; | |
c6cf4d1c | 822 | } |
c77108b8 | 823 | |
0d75c73d | 824 | if (groups) { |
9e930041 | 825 | /* FIXME: this might lead to duplicate entries, although not visible |
0d75c73d KZ |
826 | * in output, crunching a user's info multiple times is very redundant */ |
827 | while ((g = strtok(groups, ","))) { | |
828 | n = 0; | |
829 | groups = NULL; | |
c6cf4d1c | 830 | |
0d75c73d KZ |
831 | /* user specified by GID? */ |
832 | if (!str_to_uint(g, &gid)) | |
833 | grp = getgrgid(gid); | |
834 | else | |
835 | grp = getgrnam(g); | |
29cc2a55 | 836 | |
0d75c73d KZ |
837 | if (!grp) |
838 | continue; | |
839 | ||
840 | while ((u = grp->gr_mem[n++])) { | |
841 | (*ar)[i++] = xstrdup(u); | |
842 | ||
843 | if (i == *arsiz) | |
844 | *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32)); | |
845 | } | |
c6cf4d1c | 846 | } |
0d75c73d | 847 | ctl->ulist_on = 1; |
c6cf4d1c OO |
848 | } |
849 | *arsiz = i; | |
850 | return 0; | |
851 | } | |
852 | ||
853 | static void free_ctl(struct lslogins_control *ctl) | |
854 | { | |
855 | size_t n = 0; | |
856 | ||
857 | free(ctl->wtmp); | |
858 | free(ctl->btmp); | |
859 | ||
860 | while (n < ctl->ulsiz) | |
861 | free(ctl->ulist[n++]); | |
862 | ||
863 | free(ctl->ulist); | |
864 | free(ctl); | |
865 | } | |
866 | ||
867 | static struct lslogins_user *get_next_user(struct lslogins_control *ctl) | |
29cc2a55 OO |
868 | { |
869 | struct lslogins_user *u; | |
870 | errno = 0; | |
c6cf4d1c OO |
871 | while (!(u = get_user_info(ctl, NULL))) { |
872 | /* no "false" errno-s here, iff we're unable to | |
29cc2a55 OO |
873 | * get a valid user entry for any reason, quit */ |
874 | if (errno == EAGAIN) | |
875 | continue; | |
876 | return NULL; | |
877 | } | |
878 | return u; | |
879 | } | |
880 | ||
3e5cba73 KZ |
881 | static int get_user(struct lslogins_control *ctl, struct lslogins_user **user, |
882 | const char *username) | |
29cc2a55 | 883 | { |
c6cf4d1c | 884 | *user = get_user_info(ctl, username); |
123f0f5b | 885 | if (!*user) |
c6cf4d1c OO |
886 | if (IS_REAL_ERRNO(errno)) |
887 | return -1; | |
29cc2a55 OO |
888 | return 0; |
889 | } | |
8b5e2279 | 890 | |
d8b2ddb7 KZ |
891 | static int cmp_uid(const void *a, const void *b) |
892 | { | |
893 | uid_t x = ((struct lslogins_user *)a)->uid; | |
894 | uid_t z = ((struct lslogins_user *)b)->uid; | |
895 | return x > z ? 1 : (x < z ? -1 : 0); | |
896 | } | |
897 | ||
c6cf4d1c | 898 | static int create_usertree(struct lslogins_control *ctl) |
29cc2a55 | 899 | { |
c6cf4d1c OO |
900 | struct lslogins_user *user = NULL; |
901 | size_t n = 0; | |
29cc2a55 | 902 | |
0d75c73d | 903 | if (ctl->ulist_on) { |
c6cf4d1c OO |
904 | while (n < ctl->ulsiz) { |
905 | if (get_user(ctl, &user, ctl->ulist[n])) | |
29cc2a55 OO |
906 | return -1; |
907 | if (user) /* otherwise an invalid user name has probably been given */ | |
d8b2ddb7 | 908 | tsearch(user, &ctl->usertree, cmp_uid); |
c6cf4d1c | 909 | ++n; |
29cc2a55 | 910 | } |
8b5e2279 | 911 | } else { |
c6cf4d1c | 912 | while ((user = get_next_user(ctl))) |
d8b2ddb7 | 913 | tsearch(user, &ctl->usertree, cmp_uid); |
29cc2a55 OO |
914 | } |
915 | return 0; | |
916 | } | |
c6cf4d1c | 917 | |
4b6ae071 | 918 | static struct libscols_table *setup_table(struct lslogins_control *ctl) |
29cc2a55 | 919 | { |
7ee26cbf | 920 | struct libscols_table *table = scols_new_table(); |
29cc2a55 | 921 | int n = 0; |
4b6ae071 | 922 | |
7ee26cbf | 923 | if (!table) |
4b6ae071 KZ |
924 | errx(EXIT_FAILURE, _("failed to initialize output table")); |
925 | if (ctl->noheadings) | |
7ee26cbf | 926 | scols_table_enable_noheadings(table, 1); |
29cc2a55 OO |
927 | |
928 | switch(outmode) { | |
10c74524 | 929 | case OUT_COLON: |
7ee26cbf SK |
930 | scols_table_enable_raw(table, 1); |
931 | scols_table_set_column_separator(table, ":"); | |
8b5e2279 | 932 | break; |
10c74524 | 933 | case OUT_NEWLINE: |
7ee26cbf | 934 | scols_table_set_column_separator(table, "\n"); |
8b5e2279 | 935 | /* fallthrough */ |
10c74524 | 936 | case OUT_EXPORT: |
7ee26cbf | 937 | scols_table_enable_export(table, 1); |
8b5e2279 | 938 | break; |
10c74524 | 939 | case OUT_NUL: |
7ee26cbf | 940 | scols_table_set_line_separator(table, "\0"); |
8b5e2279 | 941 | /* fallthrough */ |
10c74524 | 942 | case OUT_RAW: |
7ee26cbf | 943 | scols_table_enable_raw(table, 1); |
8b5e2279 | 944 | break; |
10c74524 | 945 | case OUT_PRETTY: |
7ee26cbf | 946 | scols_table_enable_noheadings(table, 1); |
8b5e2279 KZ |
947 | default: |
948 | break; | |
29cc2a55 OO |
949 | } |
950 | ||
951 | while (n < ncolumns) { | |
bdba3fd9 KZ |
952 | int flags = coldescs[columns[n]].flag; |
953 | ||
954 | if (ctl->notrunc) | |
955 | flags &= ~SCOLS_FL_TRUNC; | |
956 | ||
7ee26cbf | 957 | if (!scols_table_new_column(table, |
bdba3fd9 KZ |
958 | coldescs[columns[n]].name, |
959 | coldescs[columns[n]].whint, | |
960 | flags)) | |
29cc2a55 OO |
961 | goto fail; |
962 | ++n; | |
963 | } | |
964 | ||
7ee26cbf | 965 | return table; |
29cc2a55 | 966 | fail: |
7ee26cbf | 967 | scols_unref_table(table); |
29cc2a55 OO |
968 | return NULL; |
969 | } | |
c6cf4d1c | 970 | |
dd7760a8 | 971 | static void fill_table(const void *u, const VISIT which, const int depth __attribute__((unused))) |
29cc2a55 | 972 | { |
29cc2a55 | 973 | struct libscols_line *ln; |
dd7760a8 OO |
974 | struct lslogins_user *user = *(struct lslogins_user **)u; |
975 | int n = 0; | |
29cc2a55 | 976 | |
5424d2a2 | 977 | if (which == preorder || which == endorder) |
dd7760a8 | 978 | return; |
29cc2a55 | 979 | |
dd7760a8 OO |
980 | ln = scols_table_new_line(tb, NULL); |
981 | while (n < ncolumns) { | |
5424d2a2 KZ |
982 | int rc = 0; |
983 | ||
dd7760a8 | 984 | switch (columns[n]) { |
f47c6d3a | 985 | case COL_USER: |
8b5e2279 KZ |
986 | rc = scols_line_set_data(ln, n, user->login); |
987 | break; | |
988 | case COL_UID: | |
989 | rc = scols_line_refer_data(ln, n, uidtostr(user->uid)); | |
990 | break; | |
f47c6d3a KZ |
991 | case COL_PWDEMPTY: |
992 | rc = scols_line_set_data(ln, n, get_status(user->pwd_empty)); | |
8b5e2279 KZ |
993 | break; |
994 | case COL_NOLOGIN: | |
10c74524 | 995 | rc = scols_line_set_data(ln, n, get_status(user->nologin)); |
8b5e2279 | 996 | break; |
f47c6d3a KZ |
997 | case COL_PWDLOCK: |
998 | rc = scols_line_set_data(ln, n, get_status(user->pwd_lock)); | |
999 | break; | |
1000 | case COL_PWDDENY: | |
1001 | rc = scols_line_set_data(ln, n, get_status(user->pwd_deny)); | |
8b5e2279 | 1002 | break; |
8b13a4d8 | 1003 | case COL_GROUP: |
8b5e2279 KZ |
1004 | rc = scols_line_set_data(ln, n, user->group); |
1005 | break; | |
8b13a4d8 | 1006 | case COL_GID: |
8b5e2279 KZ |
1007 | rc = scols_line_refer_data(ln, n, gidtostr(user->gid)); |
1008 | break; | |
8b13a4d8 | 1009 | case COL_SGROUPS: |
8b5e2279 KZ |
1010 | rc = scols_line_refer_data(ln, n, |
1011 | build_sgroups_string(user->sgroups, | |
1012 | user->nsgroups, | |
1013 | TRUE)); | |
1014 | break; | |
1015 | case COL_SGIDS: | |
1016 | rc = scols_line_refer_data(ln, n, | |
1017 | build_sgroups_string(user->sgroups, | |
1018 | user->nsgroups, | |
1019 | FALSE)); | |
1020 | break; | |
1021 | case COL_HOME: | |
1022 | rc = scols_line_set_data(ln, n, user->homedir); | |
1023 | break; | |
1024 | case COL_SHELL: | |
1025 | rc = scols_line_set_data(ln, n, user->shell); | |
1026 | break; | |
1027 | case COL_GECOS: | |
1028 | rc = scols_line_set_data(ln, n, user->gecos); | |
1029 | break; | |
1030 | case COL_LAST_LOGIN: | |
1031 | rc = scols_line_set_data(ln, n, user->last_login); | |
1032 | break; | |
1033 | case COL_LAST_TTY: | |
1034 | rc = scols_line_set_data(ln, n, user->last_tty); | |
1035 | break; | |
1036 | case COL_LAST_HOSTNAME: | |
1037 | rc = scols_line_set_data(ln, n, user->last_hostname); | |
1038 | break; | |
1039 | case COL_FAILED_LOGIN: | |
1040 | rc = scols_line_set_data(ln, n, user->failed_login); | |
1041 | break; | |
1042 | case COL_FAILED_TTY: | |
1043 | rc = scols_line_set_data(ln, n, user->failed_tty); | |
1044 | break; | |
1045 | case COL_HUSH_STATUS: | |
10c74524 | 1046 | rc = scols_line_set_data(ln, n, get_status(user->hushed)); |
8b5e2279 KZ |
1047 | break; |
1048 | case COL_PWD_WARN: | |
1049 | rc = scols_line_set_data(ln, n, user->pwd_warn); | |
1050 | break; | |
1051 | case COL_PWD_EXPIR: | |
1052 | rc = scols_line_set_data(ln, n, user->pwd_expire); | |
1053 | break; | |
1054 | case COL_PWD_CTIME: | |
1055 | rc = scols_line_set_data(ln, n, user->pwd_ctime); | |
1056 | break; | |
1057 | case COL_PWD_CTIME_MIN: | |
1058 | rc = scols_line_set_data(ln, n, user->pwd_ctime_min); | |
1059 | break; | |
1060 | case COL_PWD_CTIME_MAX: | |
1061 | rc = scols_line_set_data(ln, n, user->pwd_ctime_max); | |
1062 | break; | |
8b5e2279 | 1063 | case COL_SELINUX: |
f37b357b | 1064 | #ifdef HAVE_LIBSELINUX |
8b5e2279 | 1065 | rc = scols_line_set_data(ln, n, user->context); |
29cc2a55 | 1066 | #endif |
f37b357b | 1067 | break; |
d3a93df8 OO |
1068 | case COL_NPROCS: |
1069 | rc = scols_line_set_data(ln, n, user->nprocs); | |
1070 | break; | |
8b5e2279 KZ |
1071 | default: |
1072 | /* something went very wrong here */ | |
1073 | err(EXIT_FAILURE, _("internal error: unknown column")); | |
29cc2a55 | 1074 | } |
5424d2a2 KZ |
1075 | |
1076 | if (rc != 0) | |
1077 | err(EXIT_FAILURE, _("failed to set data")); | |
dd7760a8 | 1078 | ++n; |
29cc2a55 | 1079 | } |
dd7760a8 | 1080 | return; |
dd7760a8 | 1081 | } |
f37b357b | 1082 | #ifdef HAVE_LIBSYSTEMD |
f838efeb | 1083 | static void print_journal_tail(const char *journal_path, uid_t uid, size_t len, int time_mode) |
f37b357b KZ |
1084 | { |
1085 | sd_journal *j; | |
77006d95 | 1086 | char *match, *timestamp; |
f37b357b KZ |
1087 | uint64_t x; |
1088 | time_t t; | |
1089 | const char *identifier, *pid, *message; | |
1090 | size_t identifier_len, pid_len, message_len; | |
1091 | ||
1092 | if (journal_path) | |
1093 | sd_journal_open_directory(&j, journal_path, 0); | |
1094 | else | |
1095 | sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); | |
1096 | ||
f37b357b KZ |
1097 | xasprintf(&match, "_UID=%d", uid); |
1098 | ||
1099 | sd_journal_add_match(j, match, 0); | |
1100 | sd_journal_seek_tail(j); | |
1101 | sd_journal_previous_skip(j, len); | |
1102 | ||
1103 | do { | |
1104 | if (0 > sd_journal_get_data(j, "SYSLOG_IDENTIFIER", | |
1105 | (const void **) &identifier, &identifier_len)) | |
30ea015b | 1106 | goto done; |
f37b357b KZ |
1107 | if (0 > sd_journal_get_data(j, "_PID", |
1108 | (const void **) &pid, &pid_len)) | |
30ea015b | 1109 | goto done; |
f37b357b KZ |
1110 | if (0 > sd_journal_get_data(j, "MESSAGE", |
1111 | (const void **) &message, &message_len)) | |
30ea015b | 1112 | goto done; |
f37b357b KZ |
1113 | |
1114 | sd_journal_get_realtime_usec(j, &x); | |
1115 | t = x / 1000000; | |
77006d95 SK |
1116 | timestamp = make_time(time_mode, t); |
1117 | /* Get rid of journal entry field identifiers */ | |
f37b357b | 1118 | identifier = strchr(identifier, '=') + 1; |
77006d95 | 1119 | pid = strchr(pid, '=') + 1; |
f37b357b KZ |
1120 | message = strchr(message, '=') + 1; |
1121 | ||
77006d95 SK |
1122 | fprintf(stdout, "%s %s[%s]: %s\n", timestamp, identifier, pid, |
1123 | message); | |
1124 | free(timestamp); | |
f37b357b KZ |
1125 | } while (sd_journal_next(j)); |
1126 | ||
30ea015b | 1127 | done: |
f37b357b KZ |
1128 | free(match); |
1129 | sd_journal_flush_matches(j); | |
1130 | sd_journal_close(j); | |
1131 | } | |
1132 | #endif | |
8b5e2279 | 1133 | |
7ee26cbf | 1134 | static int print_pretty(struct libscols_table *table) |
c77108b8 OO |
1135 | { |
1136 | struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); | |
1137 | struct libscols_column *col; | |
1138 | struct libscols_cell *data; | |
1139 | struct libscols_line *ln; | |
1140 | const char *hstr, *dstr; | |
1141 | int n = 0; | |
1142 | ||
7ee26cbf SK |
1143 | ln = scols_table_get_line(table, 0); |
1144 | while (!scols_table_next_column(table, itr, &col)) { | |
c77108b8 OO |
1145 | |
1146 | data = scols_line_get_cell(ln, n); | |
c6cf4d1c | 1147 | |
3e5cba73 | 1148 | hstr = _(coldescs[columns[n]].pretty_name); |
c77108b8 OO |
1149 | dstr = scols_cell_get_data(data); |
1150 | ||
10c74524 | 1151 | if (dstr) |
74fddd86 | 1152 | printf("%s:%*c%-36s\n", hstr, 35 - (int)strlen(hstr), ' ', dstr); |
c77108b8 OO |
1153 | ++n; |
1154 | } | |
1155 | ||
1156 | scols_free_iter(itr); | |
1157 | return 0; | |
1158 | ||
1159 | } | |
8b5e2279 | 1160 | |
c6cf4d1c | 1161 | static int print_user_table(struct lslogins_control *ctl) |
dd7760a8 | 1162 | { |
4b6ae071 | 1163 | tb = setup_table(ctl); |
dd7760a8 OO |
1164 | if (!tb) |
1165 | return -1; | |
1166 | ||
c6cf4d1c | 1167 | twalk(ctl->usertree, fill_table); |
f37b357b | 1168 | if (outmode == OUT_PRETTY) { |
c77108b8 | 1169 | print_pretty(tb); |
f37b357b KZ |
1170 | #ifdef HAVE_LIBSYSTEMD |
1171 | fprintf(stdout, _("\nLast logs:\n")); | |
f838efeb | 1172 | print_journal_tail(ctl->journal_path, ctl->uid, 3, ctl->time_mode); |
f37b357b KZ |
1173 | fputc('\n', stdout); |
1174 | #endif | |
1175 | } else | |
c77108b8 | 1176 | scols_print_table(tb); |
29cc2a55 | 1177 | return 0; |
29cc2a55 OO |
1178 | } |
1179 | ||
c6cf4d1c | 1180 | static void free_user(void *f) |
dd7760a8 | 1181 | { |
c6cf4d1c OO |
1182 | struct lslogins_user *u = f; |
1183 | free(u->login); | |
1184 | free(u->group); | |
1185 | free(u->gecos); | |
1186 | free(u->sgroups); | |
1187 | free(u->pwd_ctime); | |
1188 | free(u->pwd_warn); | |
1189 | free(u->pwd_ctime_min); | |
1190 | free(u->pwd_ctime_max); | |
1191 | free(u->last_login); | |
1192 | free(u->last_tty); | |
1193 | free(u->last_hostname); | |
1194 | free(u->failed_login); | |
1195 | free(u->failed_tty); | |
1196 | free(u->homedir); | |
1197 | free(u->shell); | |
1198 | free(u->pwd_status); | |
1199 | #ifdef HAVE_LIBSELINUX | |
1200 | freecon(u->context); | |
1201 | #endif | |
dd7760a8 OO |
1202 | free(u); |
1203 | } | |
8b5e2279 | 1204 | |
cdf3896a SK |
1205 | static int parse_time_mode(const char *optarg) |
1206 | { | |
1207 | struct lslogins_timefmt { | |
1208 | const char *name; | |
1209 | const int val; | |
1210 | }; | |
1211 | static const struct lslogins_timefmt timefmts[] = { | |
1212 | {"iso", TIME_ISO}, | |
1213 | {"full", TIME_FULL}, | |
1214 | {"short", TIME_SHORT}, | |
1215 | }; | |
1216 | size_t i; | |
8b5e2279 | 1217 | |
cdf3896a SK |
1218 | for (i = 0; i < ARRAY_SIZE(timefmts); i++) { |
1219 | if (strcmp(timefmts[i].name, optarg) == 0) | |
1220 | return timefmts[i].val; | |
1221 | } | |
1222 | errx(EXIT_FAILURE, _("unknown time format: %s"), optarg); | |
1223 | } | |
c6cf4d1c | 1224 | |
512abd56 KZ |
1225 | static void __attribute__((__noreturn__)) usage(FILE *out) |
1226 | { | |
1227 | size_t i; | |
1228 | ||
1229 | fputs(USAGE_HEADER, out); | |
1230 | fprintf(out, _(" %s [options]\n"), program_invocation_short_name); | |
1231 | ||
451dbcfa BS |
1232 | fputs(USAGE_SEPARATOR, out); |
1233 | fputs(_("Display information about known users in the system.\n"), out); | |
1234 | ||
512abd56 | 1235 | fputs(USAGE_OPTIONS, out); |
74fddd86 KZ |
1236 | fputs(_(" -a, --acc-expiration display info about passwords expiration\n"), out); |
1237 | fputs(_(" -c, --colon-separate display data in a format similar to /etc/passwd\n"), out); | |
1238 | fputs(_(" -e, --export display in an export-able output format\n"), out); | |
09af3db4 | 1239 | fputs(_(" -f, --failed display data about the users' last failed logins\n"), out); |
228bb39a | 1240 | fputs(_(" -G, --supp-groups display information about groups\n"), out); |
74fddd86 | 1241 | fputs(_(" -g, --groups=<groups> display users belonging to a group in <groups>\n"), out); |
b3f4f056 | 1242 | fputs(_(" -L, --last show info about the users' last login sessions\n"), out); |
74fddd86 | 1243 | fputs(_(" -l, --logins=<logins> display only users from <logins>\n"), out); |
74fddd86 | 1244 | fputs(_(" -n, --newline display each piece of information on a new line\n"), out); |
4b6ae071 | 1245 | fputs(_(" --noheadings don't print headings\n"), out); |
74fddd86 KZ |
1246 | fputs(_(" --notruncate don't truncate output\n"), out); |
1247 | fputs(_(" -o, --output[=<list>] define the columns to output\n"), out); | |
8b13a4d8 KZ |
1248 | fputs(_(" -p, --pwd display information related to login by password.\n"), out); |
1249 | fputs(_(" -r, --raw display in raw mode\n"), out); | |
74fddd86 KZ |
1250 | fputs(_(" -s, --system-accs display system accounts\n"), out); |
1251 | fputs(_(" --time-format=<type> display dates in short, full or iso format\n"), out); | |
1252 | fputs(_(" -u, --user-accs display user accounts\n"), out); | |
74fddd86 | 1253 | fputs(_(" -Z, --context display SELinux contexts\n"), out); |
b3f4f056 | 1254 | fputs(_(" -z, --print0 delimit user entries with a nul character\n"), out); |
74fddd86 KZ |
1255 | fputs(_(" --wtmp-file <path> set an alternate path for wtmp\n"), out); |
1256 | fputs(_(" --btmp-file <path> set an alternate path for btmp\n"), out); | |
512abd56 KZ |
1257 | fputs(USAGE_SEPARATOR, out); |
1258 | fputs(USAGE_HELP, out); | |
1259 | fputs(USAGE_VERSION, out); | |
1260 | ||
1261 | fprintf(out, _("\nAvailable columns:\n")); | |
1262 | ||
1263 | for (i = 0; i < ARRAY_SIZE(coldescs); i++) | |
bdba3fd9 KZ |
1264 | fprintf(out, " %14s %s\n", coldescs[i].name, |
1265 | _(coldescs[i].help)); | |
512abd56 | 1266 | |
a587cc55 | 1267 | fprintf(out, USAGE_MAN_TAIL("lslogins(1)")); |
512abd56 KZ |
1268 | |
1269 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
1270 | } | |
1271 | ||
ab1cfad5 OO |
1272 | int main(int argc, char *argv[]) |
1273 | { | |
ed374569 | 1274 | int c, opt_o = 0; |
ab1cfad5 | 1275 | char *logins = NULL, *groups = NULL; |
dd7760a8 | 1276 | char *path_wtmp = _PATH_WTMP, *path_btmp = _PATH_BTMP; |
c6cf4d1c | 1277 | struct lslogins_control *ctl = xcalloc(1, sizeof(struct lslogins_control)); |
ed374569 | 1278 | size_t i; |
ab1cfad5 OO |
1279 | |
1280 | /* long only options. */ | |
1281 | enum { | |
dfd4359b | 1282 | OPT_WTMP = CHAR_MAX + 1, |
dd7760a8 | 1283 | OPT_BTMP, |
c77108b8 | 1284 | OPT_NOTRUNC, |
4b6ae071 | 1285 | OPT_NOHEAD, |
c20ffb8f | 1286 | OPT_TIME_FMT, |
ab1cfad5 | 1287 | }; |
c6cf4d1c | 1288 | |
ab1cfad5 OO |
1289 | static const struct option longopts[] = { |
1290 | { "acc-expiration", no_argument, 0, 'a' }, | |
512abd56 | 1291 | { "colon-separate", no_argument, 0, 'c' }, |
ab1cfad5 OO |
1292 | { "export", no_argument, 0, 'e' }, |
1293 | { "failed", no_argument, 0, 'f' }, | |
1294 | { "groups", required_argument, 0, 'g' }, | |
1295 | { "help", no_argument, 0, 'h' }, | |
1296 | { "logins", required_argument, 0, 'l' }, | |
8b13a4d8 | 1297 | { "supp-groups", no_argument, 0, 'G' }, |
ab1cfad5 | 1298 | { "newline", no_argument, 0, 'n' }, |
c77108b8 | 1299 | { "notruncate", no_argument, 0, OPT_NOTRUNC }, |
4b6ae071 | 1300 | { "noheadings", no_argument, 0, OPT_NOHEAD }, |
29cc2a55 | 1301 | { "output", required_argument, 0, 'o' }, |
b3f4f056 | 1302 | { "last", no_argument, 0, 'L', }, |
ab1cfad5 | 1303 | { "raw", no_argument, 0, 'r' }, |
dd7760a8 | 1304 | { "system-accs", no_argument, 0, 's' }, |
c20ffb8f | 1305 | { "time-format", required_argument, 0, OPT_TIME_FMT }, |
29cc2a55 | 1306 | { "user-accs", no_argument, 0, 'u' }, |
b3f4f056 | 1307 | { "version", no_argument, 0, 'V' }, |
8b13a4d8 | 1308 | { "pwd", no_argument, 0, 'p' }, |
ab1cfad5 | 1309 | { "print0", no_argument, 0, 'z' }, |
c77108b8 OO |
1310 | { "wtmp-file", required_argument, 0, OPT_WTMP }, |
1311 | { "btmp-file", required_argument, 0, OPT_BTMP }, | |
29cc2a55 OO |
1312 | #ifdef HAVE_LIBSELINUX |
1313 | { "context", no_argument, 0, 'Z' }, | |
1314 | #endif | |
ab1cfad5 OO |
1315 | { NULL, 0, 0, 0 } |
1316 | }; | |
1317 | ||
1318 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ | |
b3f4f056 KZ |
1319 | { 'G', 'o' }, |
1320 | { 'L', 'o' }, | |
1321 | { 'Z', 'o' }, | |
1322 | { 'a', 'o' }, | |
1323 | { 'c','n','r','z' }, | |
1324 | { 'o', 'p' }, | |
ab1cfad5 OO |
1325 | { 0 } |
1326 | }; | |
1327 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
1328 | ||
1329 | setlocale(LC_ALL, ""); | |
1330 | bindtextdomain(PACKAGE, LOCALEDIR); | |
1331 | textdomain(PACKAGE); | |
1332 | atexit(close_stdout); | |
1333 | ||
ea24eacc | 1334 | ctl->time_mode = TIME_SHORT; |
dd7760a8 | 1335 | |
ed374569 | 1336 | /* very basic default */ |
87a144a5 KZ |
1337 | add_column(columns, ncolumns++, COL_UID); |
1338 | add_column(columns, ncolumns++, COL_USER); | |
ed374569 | 1339 | |
dfd4359b | 1340 | while ((c = getopt_long(argc, argv, "acefGg:hLl:no:prsuVzZ", |
ab1cfad5 OO |
1341 | longopts, NULL)) != -1) { |
1342 | ||
1343 | err_exclusive_options(c, longopts, excl, excl_st); | |
1344 | ||
1345 | switch (c) { | |
8b5e2279 | 1346 | case 'a': |
87a144a5 KZ |
1347 | add_column(columns, ncolumns++, COL_PWD_WARN); |
1348 | add_column(columns, ncolumns++, COL_PWD_CTIME_MIN); | |
1349 | add_column(columns, ncolumns++, COL_PWD_CTIME_MAX); | |
1350 | add_column(columns, ncolumns++, COL_PWD_CTIME); | |
1351 | add_column(columns, ncolumns++, COL_PWD_EXPIR); | |
8b5e2279 KZ |
1352 | break; |
1353 | case 'c': | |
10c74524 | 1354 | outmode = OUT_COLON; |
8b5e2279 KZ |
1355 | break; |
1356 | case 'e': | |
10c74524 | 1357 | outmode = OUT_EXPORT; |
8b5e2279 KZ |
1358 | break; |
1359 | case 'f': | |
87a144a5 KZ |
1360 | add_column(columns, ncolumns++, COL_FAILED_LOGIN); |
1361 | add_column(columns, ncolumns++, COL_FAILED_TTY); | |
8b13a4d8 KZ |
1362 | break; |
1363 | case 'G': | |
87a144a5 KZ |
1364 | add_column(columns, ncolumns++, COL_GID); |
1365 | add_column(columns, ncolumns++, COL_GROUP); | |
1366 | add_column(columns, ncolumns++, COL_SGIDS); | |
1367 | add_column(columns, ncolumns++, COL_SGROUPS); | |
8b5e2279 KZ |
1368 | break; |
1369 | case 'g': | |
1370 | groups = optarg; | |
1371 | break; | |
1372 | case 'h': | |
1373 | usage(stdout); | |
8b5e2279 | 1374 | break; |
b3f4f056 | 1375 | case 'L': |
87a144a5 KZ |
1376 | add_column(columns, ncolumns++, COL_LAST_TTY); |
1377 | add_column(columns, ncolumns++, COL_LAST_HOSTNAME); | |
1378 | add_column(columns, ncolumns++, COL_LAST_LOGIN); | |
b3f4f056 | 1379 | break; |
8b5e2279 KZ |
1380 | case 'l': |
1381 | logins = optarg; | |
1382 | break; | |
8b5e2279 | 1383 | case 'n': |
10c74524 | 1384 | outmode = OUT_NEWLINE; |
8b5e2279 KZ |
1385 | break; |
1386 | case 'o': | |
1387 | if (optarg) { | |
1388 | if (*optarg == '=') | |
1389 | optarg++; | |
1390 | ncolumns = string_to_idarray(optarg, | |
1391 | columns, ARRAY_SIZE(columns), | |
1392 | column_name_to_id); | |
1393 | if (ncolumns < 0) | |
1394 | return EXIT_FAILURE; | |
1395 | } | |
ed374569 | 1396 | opt_o = 1; |
8b5e2279 KZ |
1397 | break; |
1398 | case 'r': | |
10c74524 | 1399 | outmode = OUT_RAW; |
8b5e2279 | 1400 | break; |
8b5e2279 KZ |
1401 | case 's': |
1402 | ctl->SYS_UID_MIN = getlogindefs_num("SYS_UID_MIN", UL_SYS_UID_MIN); | |
1403 | ctl->SYS_UID_MAX = getlogindefs_num("SYS_UID_MAX", UL_SYS_UID_MAX); | |
1404 | lslogins_flag |= F_SYSAC; | |
1405 | break; | |
8b5e2279 KZ |
1406 | case 'u': |
1407 | ctl->UID_MIN = getlogindefs_num("UID_MIN", UL_UID_MIN); | |
1408 | ctl->UID_MAX = getlogindefs_num("UID_MAX", UL_UID_MAX); | |
1409 | lslogins_flag |= F_USRAC; | |
1410 | break; | |
8b13a4d8 | 1411 | case 'p': |
87a144a5 KZ |
1412 | add_column(columns, ncolumns++, COL_PWDEMPTY); |
1413 | add_column(columns, ncolumns++, COL_PWDLOCK); | |
1414 | add_column(columns, ncolumns++, COL_PWDDENY); | |
1415 | add_column(columns, ncolumns++, COL_NOLOGIN); | |
1416 | add_column(columns, ncolumns++, COL_HUSH_STATUS); | |
8b5e2279 KZ |
1417 | break; |
1418 | case 'z': | |
10c74524 | 1419 | outmode = OUT_NUL; |
8b5e2279 KZ |
1420 | break; |
1421 | case OPT_WTMP: | |
1422 | path_wtmp = optarg; | |
1423 | break; | |
1424 | case OPT_BTMP: | |
1425 | path_btmp = optarg; | |
1426 | break; | |
1427 | case OPT_NOTRUNC: | |
bdba3fd9 | 1428 | ctl->notrunc = 1; |
8b5e2279 | 1429 | break; |
4b6ae071 KZ |
1430 | case OPT_NOHEAD: |
1431 | ctl->noheadings = 1; | |
1432 | break; | |
8b5e2279 | 1433 | case OPT_TIME_FMT: |
cdf3896a | 1434 | ctl->time_mode = parse_time_mode(optarg); |
8b5e2279 | 1435 | break; |
b3f4f056 KZ |
1436 | case 'V': |
1437 | printf(UTIL_LINUX_VERSION); | |
1438 | return EXIT_SUCCESS; | |
8b5e2279 | 1439 | case 'Z': |
37019cbc | 1440 | { |
29cc2a55 | 1441 | #ifdef HAVE_LIBSELINUX |
37019cbc KZ |
1442 | int sl = is_selinux_enabled(); |
1443 | if (sl < 0) | |
1444 | warn(_("failed to request selinux state")); | |
1445 | else | |
1446 | ctl->selinux_enabled = sl == 1; | |
29cc2a55 | 1447 | #endif |
87a144a5 | 1448 | add_column(columns, ncolumns++, COL_SELINUX); |
8b5e2279 | 1449 | break; |
37019cbc | 1450 | } |
8b5e2279 KZ |
1451 | default: |
1452 | usage(stderr); | |
ab1cfad5 OO |
1453 | } |
1454 | } | |
c20ffb8f | 1455 | |
c77108b8 OO |
1456 | if (argc - optind == 1) { |
1457 | if (strchr(argv[optind], ',')) | |
3e5cba73 | 1458 | errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users.")); |
c77108b8 | 1459 | logins = argv[optind]; |
10c74524 | 1460 | outmode = OUT_PRETTY; |
8b5e2279 | 1461 | } else if (argc != optind) |
1025d21e | 1462 | errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users.")); |
ab1cfad5 | 1463 | |
710ed55d KZ |
1464 | scols_init_debug(0); |
1465 | ||
29cc2a55 | 1466 | /* lslogins -u -s == lslogins */ |
c6cf4d1c OO |
1467 | if (lslogins_flag & F_USRAC && lslogins_flag & F_SYSAC) |
1468 | lslogins_flag &= ~(F_USRAC | F_SYSAC); | |
29cc2a55 | 1469 | |
27e6e446 | 1470 | if (outmode == OUT_PRETTY && !opt_o) { |
8b13a4d8 | 1471 | /* all columns for lslogins <username> */ |
27e6e446 | 1472 | for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) |
10c74524 KZ |
1473 | columns[ncolumns++] = i; |
1474 | ||
ed374569 KZ |
1475 | } else if (ncolumns == 2 && !opt_o) { |
1476 | /* default colummns */ | |
87a144a5 KZ |
1477 | add_column(columns, ncolumns++, COL_NPROCS); |
1478 | add_column(columns, ncolumns++, COL_PWDLOCK); | |
1479 | add_column(columns, ncolumns++, COL_PWDDENY); | |
1480 | add_column(columns, ncolumns++, COL_LAST_LOGIN); | |
1481 | add_column(columns, ncolumns++, COL_GECOS); | |
ed374569 | 1482 | } |
ed374569 KZ |
1483 | |
1484 | if (require_wtmp()) | |
c6cf4d1c | 1485 | parse_wtmp(ctl, path_wtmp); |
ed374569 | 1486 | if (require_btmp()) |
c6cf4d1c OO |
1487 | parse_btmp(ctl, path_btmp); |
1488 | ||
0d75c73d KZ |
1489 | if (logins || groups) |
1490 | get_ulist(ctl, logins, groups); | |
29cc2a55 | 1491 | |
c6cf4d1c | 1492 | if (create_usertree(ctl)) |
29cc2a55 OO |
1493 | return EXIT_FAILURE; |
1494 | ||
c6cf4d1c OO |
1495 | print_user_table(ctl); |
1496 | ||
1497 | scols_unref_table(tb); | |
1498 | tdestroy(ctl->usertree, free_user); | |
1499 | free_ctl(ctl); | |
1500 | ||
ab1cfad5 OO |
1501 | return EXIT_SUCCESS; |
1502 | } |