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