Python-скрипт для автоматичної зміни значення у файлі служби SystemD

Цей простий Python-скрипт автоматично змінює TimeoutStartSec у будь-якому файлі служби SystemD.

Викликайте так:

ensure_timeout_start_usage.sh
python ensure_timeout_start.py /etc/systemd/system/mynodejs.service

Повний вихідний код

ensure_timeout_start.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ensure_timeout_start.py

Ця робота присвячена суспільному надбанню згідно з присвятою суспільному надбанню Creative Commons "CC0 1.0 Universal" (CC0 1.0).

Автор: Uli Köhler

SPDX-License-Identifier: CC0-1.0

Забезпечує наявність TimeoutStartSec=600 у розділі [Service] файлів модулів systemd.
- Якщо [Service] відсутній, його буде додано.
- Якщо TimeoutStartSec існує (будь-яке значення), його буде встановлено на 600.
- Якщо TimeoutStartSec відсутній, його буде додано до [Service].

Використання:
  python ensure_timeout_start.py my1.service my2.service
  python ensure_timeout_start.py /etc/systemd/system/*.service --dry-run
"""

import argparse
import os
import shutil
import sys
from configupdater import ConfigUpdater


def ensure_timeout_start(filepath: str, value: str = "600", dry_run: bool = False, backup_ext: str = ".bak") -> bool:
    """
    Повертає True, якщо потрібна була зміна (і її виконано або вона була б виконана в dry-run), False, якщо вже відповідає вимогам.
    """
    updater = ConfigUpdater()
    try:
        updater.read(filepath, encoding="utf-8")
    except FileNotFoundError:
        print(f"ПОМИЛКА: Файл не знайдено: {filepath}", file=sys.stderr)
        return False

    changed = False

    # Забезпечити існування розділу [Service]
    if not updater.has_section("Service"):
        updater.add_section("Service")
        changed = True

    service = updater["Service"]

    if service.has_option("TimeoutStartSec"):
        current_val = service.get("TimeoutStartSec").value.strip()
        if current_val != value:
            service["TimeoutStartSec"].value = value
            changed = True
    else:
        service["TimeoutStartSec"] = value
        changed = True

    if dry_run:
        return changed

    if changed:
        # Створити резервну копію перед перезаписом
        if backup_ext:
            backup_path = filepath + backup_ext
            shutil.copy2(filepath, backup_path)

        tmp_path = filepath + ".tmp"
        with open(tmp_path, "w", encoding="utf-8", newline="") as f:
            updater.write(f)

        try:
            st = os.stat(filepath)
            os.chmod(tmp_path, st.st_mode)
            try:
                os.chown(tmp_path, st.st_uid, st.st_gid)  # може вимагати root
            except PermissionError:
                pass
        except FileNotFoundError:
            pass

        os.replace(tmp_path, filepath)

    return changed


def main():
    parser = argparse.ArgumentParser(
        description="Забезпечує TimeoutStartSec=600 у розділі [Service] одного або декількох файлів модулів systemd."
    )
    parser.add_argument("unit_files", nargs="+", help="Шлях(и) до файлу(ів) модуля")
    parser.add_argument("--dry-run", action="store_true", help="Показати, чи буде зроблено зміну, без запису")
    parser.add_argument("--no-backup", action="store_true", help="Не створювати резервні копії файлів")
    parser.add_argument("--value", default="600", help="Значення для TimeoutStartSec (за замовчуванням: 600)")

    args = parser.parse_args()
    backup_ext = "" if args.no_backup else ".bak"

    exit_code = 0
    for path in args.unit_files:
        changed = ensure_timeout_start(path, value=args.value, dry_run=args.dry_run, backup_ext=backup_ext)
        if args.dry_run:
            print(f"{path}: {'ПОТРІБНА ЗМІНА' if changed else 'ЗМІНА НЕ ПОТРІБНА'}")
        else:
            print(f"{path}: {'ОНОВЛЕНО' if changed else 'ВЖЕ ВІДПОВІДАЄ'}")
        if not os.path.exists(path):
            exit_code = 2

    sys.exit(exit_code)


if __name__ == "__main__":
    main()

Дивіться схожі статті за категоріями: Linux Python