""" DDNS Service - Dynamic DNS update service. Main executable for CLI and daemon mode. """ import argparse import sys from . import __version__ from .app import Application from .cli import ( cmd_cleanup, cmd_hostname_add, cmd_hostname_delete, cmd_hostname_list, cmd_hostname_modify, cmd_user_add, cmd_user_delete, cmd_user_email, cmd_user_list, cmd_user_passwd, ) from .config import ConfigError, find_config_file, load_config from .logging import disable_logging, setup_logging from .server import run_daemon def build_parser(): """Build the argument parser.""" parser = argparse.ArgumentParser( description="DDNS Service - Dynamic DNS update service", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( "--version", action="version", version=f"%(prog)s {__version__}" ) parser.add_argument("-c", "--config", help="Path to config file") parser.add_argument( "-d", "--daemon", action="store_true", help="Run as daemon" ) parser.add_argument( "--init-db", action="store_true", help="Initialize database" ) parser.add_argument( "--debug", action="store_true", help="Enable debug logging" ) subparsers = parser.add_subparsers(dest="command", title="commands") # User commands user_parser = subparsers.add_parser("user", help="User management") user_subparsers = user_parser.add_subparsers(dest="user_command") user_list = user_subparsers.add_parser("list", help="List users") user_list.set_defaults(func=cmd_user_list) user_add = user_subparsers.add_parser("add", help="Add user") user_add.add_argument("username", help="Username") user_add.add_argument("email", help="Email address") user_add.set_defaults(func=cmd_user_add) user_delete = user_subparsers.add_parser("delete", help="Delete user") user_delete.add_argument("username", help="Username") user_delete.set_defaults(func=cmd_user_delete) user_passwd = user_subparsers.add_parser("passwd", help="Change password") user_passwd.add_argument("username", help="Username") user_passwd.set_defaults(func=cmd_user_passwd) user_email = user_subparsers.add_parser("email", help="Update email") user_email.add_argument("username", help="Username") user_email.add_argument("email", help="New email address") user_email.set_defaults(func=cmd_user_email) # Hostname commands hostname_parser = subparsers.add_parser("hostname", help="Hostname management") hostname_subparsers = hostname_parser.add_subparsers(dest="hostname_command") hostname_list = hostname_subparsers.add_parser("list", help="List hostnames") hostname_list.add_argument("--user", help="Filter by username") hostname_list.set_defaults(func=cmd_hostname_list) hostname_add = hostname_subparsers.add_parser("add", help="Add hostname") hostname_add.add_argument("username", help="Username") hostname_add.add_argument("hostname", help="Hostname (FQDN)") hostname_add.add_argument("zone", help="DNS zone") hostname_add.add_argument("--dns-ttl", type=int, help="DNS record TTL") hostname_add.add_argument("--expiry-ttl", type=int, help="Expiry TTL") hostname_add.set_defaults(func=cmd_hostname_add) hostname_delete = hostname_subparsers.add_parser( "delete", help="Delete hostname" ) hostname_delete.add_argument("hostname", help="Hostname (FQDN)") hostname_delete.add_argument("zone", help="DNS zone") hostname_delete.set_defaults(func=cmd_hostname_delete) hostname_modify = hostname_subparsers.add_parser( "modify", help="Modify hostname" ) hostname_modify.add_argument("hostname", help="Hostname (FQDN)") hostname_modify.add_argument("zone", help="DNS zone") hostname_modify.add_argument("--dns-ttl", type=int, help="DNS record TTL") hostname_modify.add_argument("--expiry-ttl", type=int, help="Expiry TTL") hostname_modify.set_defaults(func=cmd_hostname_modify) # Cleanup command cleanup_parser = subparsers.add_parser("cleanup", help="Run cleanup manually") cleanup_parser.set_defaults(func=cmd_cleanup) return parser def main(): """Main entry point.""" parser = build_parser() args = parser.parse_args() # Load config try: config_path = find_config_file(args.config) config = load_config(config_path) except ConfigError as e: print(f"Error: {e}", file=sys.stderr) return 1 # Setup logging based on mode if args.daemon: log_level = "DEBUG" if args.debug else config["daemon"]["log_level"] setup_logging( level=log_level, target=config["daemon"]["log_target"], syslog_socket=config["daemon"]["syslog_socket"], syslog_facility=config["daemon"]["syslog_facility"], log_file=config["daemon"]["log_file"], log_file_size=config["daemon"]["log_file_size"], log_versions=config["daemon"]["log_versions"], ) else: if config["daemon"]["log_target"] == "stdout" and not args.debug: disable_logging() else: log_level = "DEBUG" if args.debug else config["daemon"]["log_level"] setup_logging( level=log_level, target=config["daemon"]["log_target"], syslog_socket=config["daemon"]["syslog_socket"], syslog_facility=config["daemon"]["syslog_facility"], log_file=config["daemon"]["log_file"], log_file_size=config["daemon"]["log_file_size"], log_versions=config["daemon"]["log_versions"], ) # Create application instance app = Application(config, config_path) # Initialize database try: app.init_database() except Exception as e: print(f"Error: Database initialization failed: {e}", file=sys.stderr) return 1 # Handle --init-db if args.init_db: return # Handle --daemon if args.daemon: try: # Initialize all services for daemon mode app.init_dns() app.init_email() app.init_rate_limiters() run_daemon(app) return 0 except Exception as e: print(f"Error: Daemon error: {e}", file=sys.stderr) return 1 # Handle subcommands if args.command and hasattr(args, "func"): return args.func(args, app) # No command specified parser.print_help() return 0 if __name__ == "__main__": sys.exit(main())