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