From 00b716563e55e4a71c3363a56763b544b6a91481 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Tue, 26 May 2026 01:39:38 +0900 Subject: [PATCH] Implement basic passwd parser --- main.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test.passwd | 33 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100755 main.py create mode 100644 test.passwd diff --git a/main.py b/main.py new file mode 100755 index 0000000..5d6699f --- /dev/null +++ b/main.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +import argparse +import json +from pathlib import Path + + +def main(): + parser = argparse.ArgumentParser( + description="Convert a passwd file to a directory of systemd JSON user records.", + ) + parser.add_argument( + "passwd_file", + type=Path, + help="Path to the passwd file.", + ) + parser.add_argument( + "output_dir", + type=Path, + help="Path to a directory to store json user records.", + ) + args = parser.parse_args() + + parsed_users = parse_passwd_file(args.passwd_file) + + for user in parsed_users: + print(json.dumps(user, indent=2)) + + +def parse_passwd_file(passwd_file: Path) -> list[dict]: + users = [] + with passwd_file.open("r") as f: + for line in f: + if line.startswith("#") or not line.strip(): + continue + parts = line.strip().split(":") + if len(parts) < 7: + print(f"Warning: Skipping malformed line: {line.strip()}") + continue + user = { + "name": parts[0], + "uid": int(parts[2]), + "gid": int(parts[3]), + "gecos": parse_gecos(parts[4]), + "home": parts[5], + "shell": parts[6], + } + users.append(user) + return users + + +def parse_gecos(gecos: str) -> dict: + parts = gecos.split(",") + return { + "full_name": parts[0] if len(parts) > 0 else "", + "room_number": parts[1] if len(parts) > 1 else "", + "work_phone": parts[2] if len(parts) > 2 else "", + "home_phone": parts[3] if len(parts) > 3 else "", + "other": parts[4] if len(parts) > 4 else "", + } + + +if __name__ == "__main__": + main() diff --git a/test.passwd b/test.passwd new file mode 100644 index 0000000..e7fb78a --- /dev/null +++ b/test.passwd @@ -0,0 +1,33 @@ +# Default system users + +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +games:x:5:60:games:/usr/games:/usr/sbin/nologin +man:x:6:12:man:/var/cache/man:/usr/sbin/nologin +lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin +mail:x:8:8:mail:/var/mail:/usr/sbin/nologin +news:x:9:9:news:/var/spool/news:/usr/sbin/nologin +uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin +proxy:x:13:13:proxy:/bin:/usr/sbin/nologin +www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin +backup:x:34:34:backup:/var/backups:/usr/sbin/nologin +list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin +irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin +gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin +nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin +_apt:x:100:65534::/nonexistent:/usr/sbin/nologin +systemd-timesync:x:101:101:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin +systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin +systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin +messagebus:x:104:105::/nonexistent:/usr/sbin/nologin +avahi:x:105:109:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin +saned:x:106:112::/var/lib/saned:/usr/sbin/nologin +xrdp:x:107:114::/run/xrdp:/usr/sbin/nologin + +# Custom users +user1:x:1000:1000:user1,,,:/home/user1:/bin/bash +user2:x:1001:1001:user2,,,+81 123 4567 8900:/home/user2:/bin/bash +user3:x:1002:1002:user3,Heimdal,,:/home/user3:/bin/zsh