]>
Commit | Line | Data |
---|---|---|
295c1a6e LP |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include "format-util.h" | |
4 | #include "fs-util.h" | |
5 | #include "group-record.h" | |
6 | #include "process-util.h" | |
7 | #include "rlimit-util.h" | |
8 | #include "strv.h" | |
9 | #include "terminal-util.h" | |
10 | #include "user-record-show.h" | |
11 | #include "user-util.h" | |
12 | #include "userdb.h" | |
13 | ||
14 | const char *user_record_state_color(const char *state) { | |
15 | if (STR_IN_SET(state, "unfixated", "absent")) | |
16 | return ansi_grey(); | |
17 | else if (streq(state, "active")) | |
18 | return ansi_highlight_green(); | |
19 | else if (streq(state, "locked")) | |
20 | return ansi_highlight_yellow(); | |
21 | ||
22 | return NULL; | |
23 | } | |
24 | ||
25 | void user_record_show(UserRecord *hr, bool show_full_group_info) { | |
26 | const char *hd, *ip, *shell; | |
27 | UserStorage storage; | |
28 | usec_t t; | |
29 | size_t k; | |
30 | int r, b; | |
31 | ||
32 | printf(" User name: %s\n", | |
33 | user_record_user_name_and_realm(hr)); | |
34 | ||
35 | if (hr->state) { | |
36 | const char *color; | |
37 | ||
38 | color = user_record_state_color(hr->state); | |
39 | ||
40 | printf(" State: %s%s%s\n", | |
41 | strempty(color), hr->state, color ? ansi_normal() : ""); | |
42 | } | |
43 | ||
44 | printf(" Disposition: %s\n", user_disposition_to_string(user_record_disposition(hr))); | |
45 | ||
46 | if (hr->last_change_usec != USEC_INFINITY) { | |
47 | char buf[FORMAT_TIMESTAMP_MAX]; | |
48 | printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec)); | |
49 | } | |
50 | ||
51 | if (hr->last_password_change_usec != USEC_INFINITY && | |
52 | hr->last_password_change_usec != hr->last_change_usec) { | |
53 | char buf[FORMAT_TIMESTAMP_MAX]; | |
54 | printf(" Last Passw.: %s\n", format_timestamp(buf, sizeof(buf), hr->last_password_change_usec)); | |
55 | } | |
56 | ||
57 | r = user_record_test_blocked(hr); | |
58 | switch (r) { | |
59 | ||
60 | case -ESTALE: | |
61 | printf(" Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal()); | |
62 | break; | |
63 | ||
64 | case -ENOLCK: | |
65 | printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal()); | |
66 | break; | |
67 | ||
68 | case -EL2HLT: | |
69 | printf(" Login OK: %sno%s (record not valid yet))\n", ansi_highlight_red(), ansi_normal()); | |
70 | break; | |
71 | ||
72 | case -EL3HLT: | |
73 | printf(" Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal()); | |
74 | break; | |
75 | ||
76 | default: { | |
77 | usec_t y; | |
78 | ||
79 | if (r < 0) { | |
80 | errno = -r; | |
81 | printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal()); | |
82 | break; | |
83 | } | |
84 | ||
85 | if (is_nologin_shell(user_record_shell(hr))) { | |
86 | printf(" Login OK: %sno%s (nologin shell)\n", ansi_highlight_red(), ansi_normal()); | |
87 | break; | |
88 | } | |
89 | ||
90 | y = user_record_ratelimit_next_try(hr); | |
91 | if (y != USEC_INFINITY && y > now(CLOCK_REALTIME)) { | |
92 | printf(" Login OK: %sno%s (ratelimit)\n", ansi_highlight_red(), ansi_normal()); | |
93 | break; | |
94 | } | |
95 | ||
96 | printf(" Login OK: %syes%s\n", ansi_highlight_green(), ansi_normal()); | |
97 | break; | |
98 | }} | |
99 | ||
100 | r = user_record_test_password_change_required(hr); | |
101 | switch (r) { | |
102 | ||
103 | case -EKEYREVOKED: | |
104 | printf(" Password OK: %schange now%s\n", ansi_highlight_yellow(), ansi_normal()); | |
105 | break; | |
106 | ||
107 | case -EOWNERDEAD: | |
108 | printf(" Password OK: %sexpired%s (change now!)\n", ansi_highlight_yellow(), ansi_normal()); | |
109 | break; | |
110 | ||
111 | case -EKEYREJECTED: | |
112 | printf(" Password OK: %sexpired%s (for good)\n", ansi_highlight_red(), ansi_normal()); | |
113 | break; | |
114 | ||
115 | case -EKEYEXPIRED: | |
116 | printf(" Password OK: %sexpires soon%s\n", ansi_highlight_yellow(), ansi_normal()); | |
117 | break; | |
118 | ||
119 | case -ENETDOWN: | |
120 | printf(" Password OK: %sno timestamp%s\n", ansi_highlight_red(), ansi_normal()); | |
121 | break; | |
122 | ||
123 | case -EROFS: | |
124 | printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal()); | |
125 | break; | |
126 | ||
127 | default: | |
128 | if (r < 0) { | |
129 | errno = -r; | |
130 | printf(" Password OK: %sno%s (%m)\n", ansi_highlight_yellow(), ansi_normal()); | |
131 | break; | |
132 | } | |
133 | ||
134 | printf(" Password OK: %syes%s\n", ansi_highlight_green(), ansi_normal()); | |
135 | break; | |
136 | } | |
137 | ||
138 | if (uid_is_valid(hr->uid)) | |
139 | printf(" UID: " UID_FMT "\n", hr->uid); | |
140 | if (gid_is_valid(hr->gid)) { | |
141 | if (show_full_group_info) { | |
142 | _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; | |
143 | ||
144 | r = groupdb_by_gid(hr->gid, 0, &gr); | |
145 | if (r < 0) { | |
146 | errno = -r; | |
147 | printf(" GID: " GID_FMT " (unresolvable: %m)\n", hr->gid); | |
148 | } else | |
149 | printf(" GID: " GID_FMT " (%s)\n", hr->gid, gr->group_name); | |
150 | } else | |
151 | printf(" GID: " GID_FMT "\n", hr->gid); | |
152 | } else if (uid_is_valid(hr->uid)) /* Show UID as GID if not separately configured */ | |
153 | printf(" GID: " GID_FMT "\n", (gid_t) hr->uid); | |
154 | ||
155 | if (show_full_group_info) { | |
156 | _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; | |
157 | ||
158 | r = membershipdb_by_user(hr->user_name, 0, &iterator); | |
159 | if (r < 0) { | |
160 | errno = -r; | |
161 | printf(" Aux. Groups: (can't acquire: %m)\n"); | |
162 | } else { | |
163 | const char *prefix = " Aux. Groups:"; | |
164 | ||
165 | for (;;) { | |
166 | _cleanup_free_ char *group = NULL; | |
167 | ||
168 | r = membershipdb_iterator_get(iterator, NULL, &group); | |
169 | if (r == -ESRCH) | |
170 | break; | |
171 | if (r < 0) { | |
172 | errno = -r; | |
173 | printf("%s (can't iterate: %m)\n", prefix); | |
174 | break; | |
175 | } | |
176 | ||
177 | printf("%s %s\n", prefix, group); | |
178 | prefix = " "; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | if (hr->real_name && !streq(hr->real_name, hr->user_name)) | |
184 | printf(" Real Name: %s\n", hr->real_name); | |
185 | ||
186 | hd = user_record_home_directory(hr); | |
187 | if (hd) | |
188 | printf(" Directory: %s\n", hd); | |
189 | ||
190 | storage = user_record_storage(hr); | |
191 | if (storage >= 0) /* Let's be political, and clarify which storage we like, and which we don't. About CIFS we don't complain. */ | |
192 | printf(" Storage: %s%s\n", user_storage_to_string(storage), | |
193 | storage == USER_LUKS ? " (strong encryption)" : | |
194 | storage == USER_FSCRYPT ? " (weak encryption)" : | |
195 | IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME) ? " (no encryption)" : ""); | |
196 | ||
197 | ip = user_record_image_path(hr); | |
198 | if (ip && !streq_ptr(ip, hd)) | |
199 | printf(" Image Path: %s\n", ip); | |
200 | ||
201 | b = user_record_removable(hr); | |
202 | if (b >= 0) | |
203 | printf(" Removable: %s\n", yes_no(b)); | |
204 | ||
205 | shell = user_record_shell(hr); | |
206 | if (shell) | |
207 | printf(" Shell: %s\n", shell); | |
208 | ||
209 | if (hr->email_address) | |
210 | printf(" Email: %s\n", hr->email_address); | |
211 | if (hr->location) | |
212 | printf(" Location: %s\n", hr->location); | |
213 | if (hr->password_hint) | |
214 | printf(" Passw. Hint: %s\n", hr->password_hint); | |
215 | if (hr->icon_name) | |
216 | printf(" Icon Name: %s\n", hr->icon_name); | |
217 | ||
218 | if (hr->time_zone) | |
219 | printf(" Time Zone: %s\n", hr->time_zone); | |
220 | ||
221 | if (hr->preferred_language) | |
222 | printf(" Language: %s\n", hr->preferred_language); | |
223 | ||
224 | if (!strv_isempty(hr->environment)) { | |
225 | char **i; | |
226 | ||
227 | STRV_FOREACH(i, hr->environment) { | |
228 | printf(i == hr->environment ? | |
229 | " Environment: %s\n" : | |
230 | " %s\n", *i); | |
231 | } | |
232 | } | |
233 | ||
234 | if (hr->locked >= 0) | |
235 | printf(" Locked: %s\n", yes_no(hr->locked)); | |
236 | ||
237 | if (hr->not_before_usec != UINT64_MAX) { | |
238 | char buf[FORMAT_TIMESTAMP_MAX]; | |
239 | printf(" Not Before: %s\n", format_timestamp(buf, sizeof(buf), hr->not_before_usec)); | |
240 | } | |
241 | ||
242 | if (hr->not_after_usec != UINT64_MAX) { | |
243 | char buf[FORMAT_TIMESTAMP_MAX]; | |
244 | printf(" Not After: %s\n", format_timestamp(buf, sizeof(buf), hr->not_after_usec)); | |
245 | } | |
246 | ||
247 | if (hr->umask != MODE_INVALID) | |
248 | printf(" UMask: 0%03o\n", hr->umask); | |
249 | ||
250 | if (nice_is_valid(hr->nice_level)) | |
251 | printf(" Nice: %i\n", hr->nice_level); | |
252 | ||
253 | for (int j = 0; j < _RLIMIT_MAX; j++) { | |
254 | if (hr->rlimits[j]) | |
255 | printf(" Limit: RLIMIT_%s=%" PRIu64 ":%" PRIu64 "\n", | |
256 | rlimit_to_string(j), (uint64_t) hr->rlimits[j]->rlim_cur, (uint64_t) hr->rlimits[j]->rlim_max); | |
257 | } | |
258 | ||
259 | if (hr->tasks_max != UINT64_MAX) | |
260 | printf(" Tasks Max: %" PRIu64 "\n", hr->tasks_max); | |
261 | ||
262 | if (hr->memory_high != UINT64_MAX) { | |
263 | char buf[FORMAT_BYTES_MAX]; | |
264 | printf(" Memory High: %s\n", format_bytes(buf, sizeof(buf), hr->memory_high)); | |
265 | } | |
266 | ||
267 | if (hr->memory_max != UINT64_MAX) { | |
268 | char buf[FORMAT_BYTES_MAX]; | |
269 | printf(" Memory Max: %s\n", format_bytes(buf, sizeof(buf), hr->memory_max)); | |
270 | } | |
271 | ||
272 | if (hr->cpu_weight != UINT64_MAX) | |
273 | printf(" CPU Weight: %" PRIu64 "\n", hr->cpu_weight); | |
274 | ||
275 | if (hr->io_weight != UINT64_MAX) | |
276 | printf(" IO Weight: %" PRIu64 "\n", hr->io_weight); | |
277 | ||
278 | if (hr->access_mode != MODE_INVALID) | |
279 | printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr)); | |
280 | ||
281 | if (storage == USER_LUKS) { | |
5e86c82a | 282 | printf("LUKS Discard: online=%s offline=%s\n", yes_no(user_record_luks_discard(hr)), yes_no(user_record_luks_offline_discard(hr))); |
295c1a6e LP |
283 | |
284 | if (!sd_id128_is_null(hr->luks_uuid)) | |
285 | printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid)); | |
286 | if (!sd_id128_is_null(hr->partition_uuid)) | |
287 | printf(" Part UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->partition_uuid)); | |
288 | if (!sd_id128_is_null(hr->file_system_uuid)) | |
289 | printf(" FS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->file_system_uuid)); | |
290 | ||
291 | if (hr->file_system_type) | |
292 | printf(" File System: %s\n", user_record_file_system_type(hr)); | |
293 | ||
294 | if (hr->luks_cipher) | |
295 | printf(" LUKS Cipher: %s\n", hr->luks_cipher); | |
296 | if (hr->luks_cipher_mode) | |
297 | printf(" Cipher Mode: %s\n", hr->luks_cipher_mode); | |
298 | if (hr->luks_volume_key_size != UINT64_MAX) | |
299 | printf(" Volume Key: %" PRIu64 "bit\n", hr->luks_volume_key_size * 8); | |
300 | ||
301 | if (hr->luks_pbkdf_type) | |
302 | printf(" PBKDF Type: %s\n", hr->luks_pbkdf_type); | |
303 | if (hr->luks_pbkdf_hash_algorithm) | |
304 | printf(" PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm); | |
305 | if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX) { | |
306 | char buf[FORMAT_TIMESPAN_MAX]; | |
307 | printf(" PBKDF Time: %s\n", format_timespan(buf, sizeof(buf), hr->luks_pbkdf_time_cost_usec, 0)); | |
308 | } | |
309 | if (hr->luks_pbkdf_memory_cost != UINT64_MAX) { | |
310 | char buf[FORMAT_BYTES_MAX]; | |
311 | printf(" PBKDF Bytes: %s\n", format_bytes(buf, sizeof(buf), hr->luks_pbkdf_memory_cost)); | |
312 | } | |
313 | if (hr->luks_pbkdf_parallel_threads != UINT64_MAX) | |
314 | printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads); | |
315 | ||
316 | } else if (storage == USER_CIFS) { | |
317 | ||
318 | if (hr->cifs_service) | |
319 | printf("CIFS Service: %s\n", hr->cifs_service); | |
320 | } | |
321 | ||
322 | if (hr->cifs_user_name) | |
323 | printf(" CIFS User: %s\n", user_record_cifs_user_name(hr)); | |
324 | if (hr->cifs_domain) | |
325 | printf(" CIFS Domain: %s\n", hr->cifs_domain); | |
326 | ||
327 | if (storage != USER_CLASSIC) | |
328 | printf(" Mount Flags: %s %s %s\n", | |
329 | hr->nosuid ? "nosuid" : "suid", | |
330 | hr->nodev ? "nodev" : "dev", | |
331 | hr->noexec ? "noexec" : "exec"); | |
332 | ||
333 | if (hr->skeleton_directory) | |
334 | printf(" Skel. Dir.: %s\n", user_record_skeleton_directory(hr)); | |
335 | ||
336 | if (hr->disk_size != UINT64_MAX) { | |
337 | char buf[FORMAT_BYTES_MAX]; | |
338 | printf(" Disk Size: %s\n", format_bytes(buf, sizeof(buf), hr->disk_size)); | |
339 | } | |
340 | ||
341 | if (hr->disk_usage != UINT64_MAX) { | |
342 | char buf[FORMAT_BYTES_MAX]; | |
f5b7d681 LP |
343 | |
344 | if (hr->disk_size != UINT64_MAX) { | |
345 | unsigned permille; | |
346 | ||
347 | permille = (unsigned) DIV_ROUND_UP(hr->disk_usage * 1000U, hr->disk_size); /* Round up! */ | |
348 | printf(" Disk Usage: %s (= %u.%01u%%)\n", | |
349 | format_bytes(buf, sizeof(buf), hr->disk_usage), | |
350 | permille / 10, permille % 10); | |
351 | } else | |
352 | printf(" Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage)); | |
295c1a6e LP |
353 | } |
354 | ||
355 | if (hr->disk_free != UINT64_MAX) { | |
356 | char buf[FORMAT_BYTES_MAX]; | |
f5b7d681 LP |
357 | |
358 | if (hr->disk_size != UINT64_MAX) { | |
c01ef54f | 359 | const char *color_on, *color_off; |
f5b7d681 LP |
360 | unsigned permille; |
361 | ||
362 | permille = (unsigned) ((hr->disk_free * 1000U) / hr->disk_size); /* Round down! */ | |
c01ef54f LP |
363 | |
364 | /* Color the output red or yellow if we are below 10% resp. 25% free. Because 10% and | |
365 | * 25% can be a lot of space still, let's additionally make some absolute | |
366 | * restrictions: 1G and 2G */ | |
367 | if (permille <= 100U && | |
368 | hr->disk_free < 1024U*1024U*1024U /* 1G */) { | |
369 | color_on = ansi_highlight_red(); | |
370 | color_off = ansi_normal(); | |
371 | } else if (permille <= 250U && | |
372 | hr->disk_free < 2U*1024U*1024U*1024U /* 2G */) { | |
373 | color_on = ansi_highlight_yellow(); | |
374 | color_off = ansi_normal(); | |
375 | } else | |
376 | color_on = color_off = ""; | |
377 | ||
378 | printf(" Disk Free: %s%s (= %u.%01u%%)%s\n", | |
379 | color_on, | |
f5b7d681 | 380 | format_bytes(buf, sizeof(buf), hr->disk_free), |
c01ef54f LP |
381 | permille / 10, permille % 10, |
382 | color_off); | |
f5b7d681 LP |
383 | } else |
384 | printf(" Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free)); | |
295c1a6e LP |
385 | } |
386 | ||
387 | if (hr->disk_floor != UINT64_MAX) { | |
388 | char buf[FORMAT_BYTES_MAX]; | |
389 | printf(" Disk Floor: %s\n", format_bytes(buf, sizeof(buf), hr->disk_floor)); | |
390 | } | |
391 | ||
392 | if (hr->disk_ceiling != UINT64_MAX) { | |
393 | char buf[FORMAT_BYTES_MAX]; | |
394 | printf("Disk Ceiling: %s\n", format_bytes(buf, sizeof(buf), hr->disk_ceiling)); | |
395 | } | |
396 | ||
397 | if (hr->good_authentication_counter != UINT64_MAX) | |
398 | printf(" Good Auth.: %" PRIu64 "\n", hr->good_authentication_counter); | |
399 | ||
400 | if (hr->last_good_authentication_usec != UINT64_MAX) { | |
401 | char buf[FORMAT_TIMESTAMP_MAX]; | |
402 | printf(" Last Good: %s\n", format_timestamp(buf, sizeof(buf), hr->last_good_authentication_usec)); | |
403 | } | |
404 | ||
405 | if (hr->bad_authentication_counter != UINT64_MAX) | |
406 | printf(" Bad Auth.: %" PRIu64 "\n", hr->bad_authentication_counter); | |
407 | ||
408 | if (hr->last_bad_authentication_usec != UINT64_MAX) { | |
409 | char buf[FORMAT_TIMESTAMP_MAX]; | |
410 | printf(" Last Bad: %s\n", format_timestamp(buf, sizeof(buf), hr->last_bad_authentication_usec)); | |
411 | } | |
412 | ||
413 | t = user_record_ratelimit_next_try(hr); | |
414 | if (t != USEC_INFINITY) { | |
415 | usec_t n = now(CLOCK_REALTIME); | |
416 | ||
417 | if (t <= n) | |
418 | printf(" Next Try: anytime\n"); | |
419 | else { | |
420 | char buf[FORMAT_TIMESPAN_MAX]; | |
421 | printf(" Next Try: %sin %s%s\n", | |
422 | ansi_highlight_red(), | |
423 | format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC), | |
424 | ansi_normal()); | |
425 | } | |
426 | } | |
427 | ||
428 | if (storage != USER_CLASSIC) { | |
429 | char buf[FORMAT_TIMESPAN_MAX]; | |
430 | printf(" Auth. Limit: %" PRIu64 " attempts per %s\n", user_record_ratelimit_burst(hr), | |
431 | format_timespan(buf, sizeof(buf), user_record_ratelimit_interval_usec(hr), 0)); | |
432 | } | |
433 | ||
434 | if (hr->enforce_password_policy >= 0) | |
435 | printf(" Passwd Pol.: %s\n", yes_no(hr->enforce_password_policy)); | |
436 | ||
437 | if (hr->password_change_min_usec != UINT64_MAX || | |
438 | hr->password_change_max_usec != UINT64_MAX || | |
439 | hr->password_change_warn_usec != UINT64_MAX || | |
440 | hr->password_change_inactive_usec != UINT64_MAX) { | |
441 | ||
442 | char buf[FORMAT_TIMESPAN_MAX]; | |
443 | printf(" Passwd Chg.:"); | |
444 | ||
445 | if (hr->password_change_min_usec != UINT64_MAX) { | |
446 | printf(" min %s", format_timespan(buf, sizeof(buf), hr->password_change_min_usec, 0)); | |
447 | ||
448 | if (hr->password_change_max_usec != UINT64_MAX) | |
449 | printf(" …"); | |
450 | } | |
451 | ||
452 | if (hr->password_change_max_usec != UINT64_MAX) | |
453 | printf(" max %s", format_timespan(buf, sizeof(buf), hr->password_change_max_usec, 0)); | |
454 | ||
455 | if (hr->password_change_warn_usec != UINT64_MAX) | |
456 | printf("/warn %s", format_timespan(buf, sizeof(buf), hr->password_change_warn_usec, 0)); | |
457 | ||
458 | if (hr->password_change_inactive_usec != UINT64_MAX) | |
459 | printf("/inactive %s", format_timespan(buf, sizeof(buf), hr->password_change_inactive_usec, 0)); | |
460 | ||
461 | printf("\n"); | |
462 | } | |
463 | ||
464 | if (hr->password_change_now >= 0) | |
465 | printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now)); | |
466 | ||
467 | if (!strv_isempty(hr->ssh_authorized_keys)) | |
468 | printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys)); | |
469 | ||
470 | if (!strv_isempty(hr->pkcs11_token_uri)) { | |
471 | char **i; | |
472 | ||
473 | STRV_FOREACH(i, hr->pkcs11_token_uri) | |
474 | printf(i == hr->pkcs11_token_uri ? | |
5e4fa456 | 475 | "PKCS11 Token: %s\n" : |
295c1a6e LP |
476 | " %s\n", *i); |
477 | } | |
478 | ||
5e4fa456 LP |
479 | if (hr->n_fido2_hmac_credential > 0) |
480 | printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential); | |
481 | ||
295c1a6e LP |
482 | k = strv_length(hr->hashed_password); |
483 | if (k == 0) | |
484 | printf(" Passwords: %snone%s\n", | |
485 | user_record_disposition(hr) == USER_REGULAR ? ansi_highlight_yellow() : ansi_normal(), ansi_normal()); | |
486 | else | |
487 | printf(" Passwords: %zu\n", k); | |
488 | ||
489 | if (hr->signed_locally >= 0) | |
490 | printf(" Local Sig.: %s\n", yes_no(hr->signed_locally)); | |
491 | ||
492 | if (hr->stop_delay_usec != UINT64_MAX) { | |
493 | char buf[FORMAT_TIMESPAN_MAX]; | |
494 | printf(" Stop Delay: %s\n", format_timespan(buf, sizeof(buf), hr->stop_delay_usec, 0)); | |
495 | } | |
496 | ||
497 | if (hr->auto_login >= 0) | |
498 | printf("Autom. Login: %s\n", yes_no(hr->auto_login)); | |
499 | ||
500 | if (hr->kill_processes >= 0) | |
501 | printf(" Kill Proc.: %s\n", yes_no(hr->kill_processes)); | |
502 | ||
503 | if (hr->service) | |
504 | printf(" Service: %s\n", hr->service); | |
505 | } |