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