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