optimized installer

This commit is contained in:
2026-09-17 13:29:45 +05:00
parent 01f8dbe860
commit 298124c30f
4 changed files with 1322 additions and 81 deletions
+607
View File
@@ -0,0 +1,607 @@
import json
import os
import socket
import struct
import subprocess
import threading
from pathlib import Path
from .database import get_db
SOCKET_PATH = Path("/run/parental-control.sock")
BUFFER_SIZE = 65536
POLKIT_ACTION = "org.sarlink.parentalcontrol.modify"
def get_peer_credentials(connection):
credentials = connection.getsockopt(
socket.SOL_SOCKET,
socket.SO_PEERCRED,
struct.calcsize("3i"),
)
return struct.unpack("3i", credentials)
def authorize_write(pid: int) -> bool:
try:
os.kill(pid, 0)
except OSError:
return False
try:
result = subprocess.run(
[
"pkcheck",
"--action-id",
POLKIT_ACTION,
"--process",
str(pid),
"--allow-user-interaction",
],
capture_output=True,
text=True,
check=False,
)
return result.returncode == 0
except FileNotFoundError:
return False
class IPCOperations:
@staticmethod
def get_users():
with get_db() as db:
rows = db.execute(
"""
SELECT *
FROM users
ORDER BY username COLLATE NOCASE
"""
).fetchall()
return [dict(row) for row in rows]
@staticmethod
def get_user(user_id):
with get_db() as db:
row = db.execute(
"""
SELECT *
FROM users
WHERE id = ?
""",
(user_id,),
).fetchone()
return dict(row) if row else None
@staticmethod
def get_remaining_time(user_id):
with get_db() as db:
allowance_row = db.execute(
"""
SELECT COALESCE(
allowance_seconds,
0
) AS total
FROM daily_allowances
WHERE user_id = ?
AND weekday = CAST(
strftime('%w', 'now', 'localtime')
AS INTEGER
)
""",
(user_id,),
).fetchone()
allowance = (
allowance_row["total"]
if allowance_row
else 0
)
usage_row = db.execute(
"""
SELECT COALESCE(
used_seconds,
0
) AS total
FROM usage
WHERE user_id = ?
AND date = date('now', 'localtime')
""",
(user_id,),
).fetchone()
used = (
usage_row["total"]
if usage_row
else 0
)
remaining = max(
allowance - used,
0,
)
return remaining, allowance
@staticmethod
def get_allowances(user_id):
with get_db() as db:
rows = db.execute(
"""
SELECT weekday, allowance_seconds
FROM daily_allowances
WHERE user_id = ?
""",
(user_id,),
).fetchall()
return {
row["weekday"]: row["allowance_seconds"]
for row in rows
}
@staticmethod
def get_access_windows(user_id):
with get_db() as db:
rows = db.execute(
"""
SELECT weekday, start_minute, end_minute
FROM access_windows
WHERE user_id = ?
ORDER BY weekday, start_minute
""",
(user_id,),
).fetchall()
result = {}
for row in rows:
result.setdefault(
row["weekday"],
[],
).append(
(
row["start_minute"],
row["end_minute"],
)
)
return result
@staticmethod
def create_user(
username,
enabled,
allowances,
access_windows,
):
with get_db() as db:
cursor = db.execute(
"""
INSERT INTO users (
username,
enabled
)
VALUES (?, ?)
""",
(
username,
1 if enabled else 0,
),
)
user_id = cursor.lastrowid
for weekday, seconds in allowances.items():
db.execute(
"""
INSERT INTO daily_allowances (
user_id,
weekday,
allowance_seconds
)
VALUES (?, ?, ?)
""",
(
user_id,
weekday,
seconds,
),
)
for weekday, windows in access_windows.items():
for start_minute, end_minute in windows:
db.execute(
"""
INSERT INTO access_windows (
user_id,
weekday,
start_minute,
end_minute
)
VALUES (?, ?, ?, ?)
""",
(
user_id,
weekday,
start_minute,
end_minute,
),
)
return user_id
@staticmethod
def update_user(
user_id,
enabled,
allowances,
access_windows,
):
with get_db() as db:
db.execute(
"""
UPDATE users
SET enabled = ?
WHERE id = ?
""",
(
1 if enabled else 0,
user_id,
),
)
for weekday, seconds in allowances.items():
db.execute(
"""
INSERT INTO daily_allowances (
user_id,
weekday,
allowance_seconds
)
VALUES (?, ?, ?)
ON CONFLICT(user_id, weekday)
DO UPDATE SET
allowance_seconds =
excluded.allowance_seconds
""",
(
user_id,
weekday,
seconds,
),
)
db.execute(
"""
DELETE FROM access_windows
WHERE user_id = ?
""",
(user_id,),
)
for weekday, windows in access_windows.items():
for start_minute, end_minute in windows:
db.execute(
"""
INSERT INTO access_windows (
user_id,
weekday,
start_minute,
end_minute
)
VALUES (?, ?, ?, ?)
""",
(
user_id,
weekday,
start_minute,
end_minute,
),
)
@staticmethod
def delete_user(user_id):
with get_db() as db:
db.execute(
"""
DELETE FROM users
WHERE id = ?
""",
(user_id,),
)
@staticmethod
def set_enabled(user_id, enabled):
with get_db() as db:
db.execute(
"""
UPDATE users
SET enabled = ?
WHERE id = ?
""",
(
1 if enabled else 0,
user_id,
),
)
@staticmethod
def add_temporary_time(
user_id,
seconds,
):
with get_db() as db:
db.execute(
"""
INSERT INTO temporary_grants (
user_id,
seconds,
remaining_seconds
)
VALUES (?, ?, ?)
""",
(
user_id,
seconds,
seconds,
),
)
READ_METHODS = {
"get_users",
"get_user",
"get_remaining_time",
"get_allowances",
"get_access_windows",
}
WRITE_METHODS = {
"create_user",
"update_user",
"delete_user",
"set_enabled",
"add_temporary_time",
}
def handle_request(request, pid):
method = request.get("method")
arguments = request.get("args", {})
if method in READ_METHODS:
operation = getattr(
IPCOperations,
method,
None,
)
if operation is None:
raise RuntimeError(
f"Unknown method: {method}"
)
return operation(**arguments)
if method in WRITE_METHODS:
if not authorize_write(pid):
raise PermissionError(
"Administrative authorization required"
)
operation = getattr(
IPCOperations,
method,
None,
)
if operation is None:
raise RuntimeError(
f"Unknown method: {method}"
)
return operation(**arguments)
raise RuntimeError(
f"Unknown IPC method: {method}"
)
def handle_client(connection):
try:
pid, uid, gid = get_peer_credentials(
connection
)
data = bytearray()
while True:
chunk = connection.recv(
BUFFER_SIZE
)
if not chunk:
break
data.extend(chunk)
if b"\n" in chunk:
break
if not data:
return
line = bytes(data).split(
b"\n",
1,
)[0]
request = json.loads(
line.decode("utf-8")
)
try:
result = handle_request(
request,
pid,
)
response = {
"ok": True,
"result": result,
}
except PermissionError as exc:
response = {
"ok": False,
"error": str(exc),
"error_type": "authorization",
}
except Exception as exc:
response = {
"ok": False,
"error": str(exc),
"error_type": "server",
}
payload = (
json.dumps(
response,
separators=(",", ":"),
).encode("utf-8")
+ b"\n"
)
connection.sendall(payload)
except Exception:
try:
connection.sendall(
b'{"ok":false,"error":"Invalid request",'
b'"error_type":"server"}\n'
)
except Exception:
pass
finally:
connection.close()
class IPCServer:
def __init__(
self,
socket_path=SOCKET_PATH,
):
self.socket_path = Path(
socket_path
)
self.server_socket = None
self._thread = None
self._stop_event = threading.Event()
def start(self):
if self._thread is not None:
return
self.socket_path.parent.mkdir(
parents=True,
exist_ok=True,
)
try:
self.socket_path.unlink()
except FileNotFoundError:
pass
self.server_socket = socket.socket(
socket.AF_UNIX,
socket.SOCK_STREAM,
)
self.server_socket.bind(
str(self.socket_path)
)
os.chmod(
self.socket_path,
0o666,
)
self.server_socket.listen(16)
self._stop_event.clear()
self._thread = threading.Thread(
target=self._run,
name="parental-control-ipc",
daemon=True,
)
self._thread.start()
def stop(self):
self._stop_event.set()
if self.server_socket is not None:
try:
self.server_socket.close()
except Exception:
pass
if self._thread is not None:
self._thread.join(timeout=2)
self._thread = None
self.server_socket = None
try:
self.socket_path.unlink()
except FileNotFoundError:
pass
def _run(self):
while not self._stop_event.is_set():
try:
connection, _ = (
self.server_socket.accept()
)
except OSError:
if self._stop_event.is_set():
break
continue
thread = threading.Thread(
target=handle_client,
args=(connection,),
daemon=True,
)
thread.start()
ipc_server = IPCServer()
def start_ipc_server():
ipc_server.start()
def stop_ipc_server():
ipc_server.stop()
+5
View File
@@ -2,19 +2,24 @@ import time
from .database import initialize_database from .database import initialize_database
from .scheduler import start_scheduler, stop_scheduler from .scheduler import start_scheduler, stop_scheduler
from .ipc import start_ipc_server, stop_ipc_server
def main(): def main():
initialize_database() initialize_database()
start_ipc_server()
start_scheduler() start_scheduler()
try: try:
while True: while True:
time.sleep(60) time.sleep(60)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
finally: finally:
stop_scheduler() stop_scheduler()
stop_ipc_server()
if __name__ == "__main__": if __name__ == "__main__":
+710 -78
View File
@@ -1,110 +1,525 @@
#!/bin/bash #!/bin/bash
set -e set -euo pipefail
# ========================================== # ============================================================
# Linux Parental Control Installer # Linux Parental Control Installer
# ========================================== # ============================================================
APP_NAME="Parental Control"
SERVICE_NAME="parental-control.service"
DESKTOP_ID="parental-control.desktop"
INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_DIR="$INSTALL_DIR/.venv"
PYTHON="$VENV_DIR/bin/python"
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME"
DESKTOP_FILE="/usr/share/applications/$DESKTOP_ID"
POLKIT_ACTION="org.sarlink.parentalcontrol.modify"
POLKIT_SOURCE="$INSTALL_DIR/$POLKIT_ACTION.policy"
POLKIT_DEST="/etc/polkit-1/actions/$POLKIT_ACTION.policy"
SOCKET_PATH="/run/parental-control.sock"
# ============================================================
# Output helpers
# ============================================================
info() {
echo
echo "==> $1"
}
success() {
echo " $1"
}
warning() {
echo " WARNING: $1"
}
error() {
echo
echo "ERROR: $1"
echo
exit 1
}
# ============================================================
# Root check
# ============================================================
if [ "$EUID" -ne 0 ]; then if [ "$EUID" -ne 0 ]; then
echo "Please run with sudo:" echo
echo "This installer requires administrator privileges."
echo
echo "Run:"
echo
echo " sudo ./install.sh" echo " sudo ./install.sh"
echo
exit 1 exit 1
fi fi
INSTALL_DIR="$(cd "$(dirname "$0")" && pwd)"
APP_NAME="Parental Control" # ============================================================
DESKTOP_FILE="/usr/share/applications/parental-control.desktop" # Find original user
SERVICE_FILE="/etc/systemd/system/parental-control.service" #
# When running through sudo, SUDO_USER is the person who
# launched the installer. We use this to keep the source tree
# owned by that user rather than root.
# ============================================================
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
INSTALL_USER="$SUDO_USER"
else
INSTALL_USER="$(stat -c '%U' "$INSTALL_DIR")"
fi
if ! id "$INSTALL_USER" >/dev/null 2>&1; then
error "Could not determine the normal installation user."
fi
INSTALL_GROUP="$(id -gn "$INSTALL_USER")"
# ============================================================
# Header
# ============================================================
clear 2>/dev/null || true
echo echo
echo "==========================================" echo "=========================================="
echo " Linux Parental Control Installer" echo " Linux Parental Control Installer"
echo "==========================================" echo "=========================================="
echo echo
echo "Installing from:" echo "Application:"
echo " $APP_NAME"
echo
echo "Installation directory:"
echo " $INSTALL_DIR" echo " $INSTALL_DIR"
echo echo
echo "Installation user:"
# ------------------------------------------ echo " $INSTALL_USER"
# 1. System dependencies
# ------------------------------------------
echo "[1/6] Installing system dependencies"
pacman -S --needed --noconfirm \
python \
python-pip
echo echo
echo "System dependencies installed."
# ------------------------------------------
# 2. Python virtual environment
# ------------------------------------------
echo # ============================================================
echo "[2/6] Creating Python virtual environment" # Check project files
# ============================================================
if [ ! -d "$INSTALL_DIR/.venv" ]; then info "Checking project files"
python -m venv "$INSTALL_DIR/.venv"
else REQUIRED_FILES=(
echo "Virtual environment already exists." "requirements.txt"
"parental-control.service"
"app/__init__.py"
"app/main.py"
"app/database.py"
"app/enforcement.py"
"app/scheduler.py"
"app/users.py"
"app/ipc.py"
"desktop/__init__.py"
"desktop/main.py"
"desktop/client.py"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$INSTALL_DIR/$file" ]; then
error "Required file not found: $file"
fi
done
success "All required project files found."
# ============================================================
# Check Linux
# ============================================================
info "Detecting operating system"
if [ ! -f /etc/os-release ]; then
error "/etc/os-release was not found."
fi fi
echo # shellcheck disable=SC1091
echo "Virtual environment ready." source /etc/os-release
# ------------------------------------------ OS_ID="${ID:-unknown}"
# 3. Python dependencies OS_NAME="${NAME:-Unknown Linux}"
# ------------------------------------------ OS_VERSION="${VERSION_ID:-unknown}"
echo echo " Distribution: $OS_NAME"
echo "[3/6] Installing Python packages" echo " Version: $OS_VERSION"
"$INSTALL_DIR/.venv/bin/python" -m pip install \
# ============================================================
# Check systemd
# ============================================================
info "Checking systemd"
if ! command -v systemctl >/dev/null 2>&1; then
error "systemd is required, but systemctl was not found."
fi
if [ ! -d /run/systemd/system ]; then
error "systemd is installed but does not appear to be running."
fi
success "systemd detected."
# ============================================================
# Detect package manager
# ============================================================
info "Detecting package manager"
PACKAGE_MANAGER=""
if command -v pacman >/dev/null 2>&1; then
PACKAGE_MANAGER="pacman"
elif command -v apt-get >/dev/null 2>&1; then
PACKAGE_MANAGER="apt"
elif command -v dnf >/dev/null 2>&1; then
PACKAGE_MANAGER="dnf"
elif command -v zypper >/dev/null 2>&1; then
PACKAGE_MANAGER="zypper"
elif command -v apk >/dev/null 2>&1; then
PACKAGE_MANAGER="apk"
elif command -v xbps-install >/dev/null 2>&1; then
PACKAGE_MANAGER="xbps"
else
error "No supported package manager was detected.
Supported package managers:
pacman
apt
dnf
zypper
apk
xbps-install"
fi
success "Package manager: $PACKAGE_MANAGER"
# ============================================================
# Install system packages
# ============================================================
info "Installing system dependencies"
case "$PACKAGE_MANAGER" in
pacman)
pacman -S --needed --noconfirm \
python \
python-pip \
polkit \
desktop-file-utils
;;
apt)
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y \
python3 \
python3-pip \
python3-venv \
polkitd \
policykit-1 \
desktop-file-utils
;;
dnf)
dnf install -y \
python3 \
python3-pip \
polkit \
desktop-file-utils
;;
zypper)
zypper --non-interactive install \
python3 \
python3-pip \
polkit \
desktop-file-utils
;;
apk)
apk add \
python3 \
py3-pip \
py3-virtualenv \
polkit \
desktop-file-utils
;;
xbps)
xbps-install -Sy \
python3 \
python3-pip \
polkit \
desktop-file-utils
;;
esac
success "System dependencies installed."
# ============================================================
# Find Python
# ============================================================
info "Checking Python"
SYSTEM_PYTHON=""
for candidate in python3 python; do
if command -v "$candidate" >/dev/null 2>&1; then
SYSTEM_PYTHON="$(command -v "$candidate")"
break
fi
done
if [ -z "$SYSTEM_PYTHON" ]; then
error "Python 3 could not be found."
fi
PYTHON_MAJOR="$(
"$SYSTEM_PYTHON" -c 'import sys; print(sys.version_info.major)'
)"
PYTHON_MINOR="$(
"$SYSTEM_PYTHON" -c 'import sys; print(sys.version_info.minor)'
)"
PYTHON_VERSION="$(
"$SYSTEM_PYTHON" -c \
'import sys; print(".".join(map(str, sys.version_info[:3])))'
)"
if [ "$PYTHON_MAJOR" -ne 3 ]; then
error "Python 3 is required. Found Python $PYTHON_VERSION."
fi
echo " Python: $SYSTEM_PYTHON"
echo " Version: $PYTHON_VERSION"
# ============================================================
# Check Python version for PySide6
# ============================================================
if [ "$PYTHON_MINOR" -lt 9 ]; then
error "Python $PYTHON_VERSION is too old for this application.
Python 3.9 or newer is required."
fi
success "Python version is supported."
# ============================================================
# Create/recreate virtual environment
# ============================================================
info "Preparing Python virtual environment"
if [ -x "$PYTHON" ]; then
success "Existing virtual environment is valid."
else
if [ -d "$VENV_DIR" ]; then
echo " Existing virtual environment is broken."
echo " Removing it..."
rm -rf "$VENV_DIR"
fi
echo " Creating virtual environment..."
if ! "$SYSTEM_PYTHON" -m venv "$VENV_DIR"; then
error "Could not create the Python virtual environment.
Your distribution may require an additional Python venv package."
fi
success "Virtual environment created."
fi
# ============================================================
# Install Python dependencies
# ============================================================
info "Installing Python dependencies"
"$PYTHON" -m pip install \
--disable-pip-version-check \
--upgrade pip --upgrade pip
"$INSTALL_DIR/.venv/bin/python" -m pip install \ "$PYTHON" -m pip install \
--disable-pip-version-check \
-r "$INSTALL_DIR/requirements.txt" -r "$INSTALL_DIR/requirements.txt"
echo success "Python dependencies installed."
echo "Python packages installed."
# ------------------------------------------
# 4. Install systemd service
# ------------------------------------------
echo # ============================================================
echo "[4/6] Installing systemd service" # Validate Python application
# ============================================================
info "Checking Python source files"
"$PYTHON" -m compileall \
-q \
"$INSTALL_DIR/app" \
"$INSTALL_DIR/desktop"
success "Python source validation passed."
# ============================================================
# Remove Python cache files created by installer
# ============================================================
find "$INSTALL_DIR/app" \
"$INSTALL_DIR/desktop" \
-type d \
-name "__pycache__" \
-prune \
-exec rm -rf {} + \
2>/dev/null || true
# ============================================================
# Restore source ownership
# ============================================================
info "Fixing source ownership"
chown -R \
"$INSTALL_USER:$INSTALL_GROUP" \
"$INSTALL_DIR"
success "Source tree owned by $INSTALL_USER."
# ============================================================
# Prepare data directory
# ============================================================
info "Preparing application data"
mkdir -p "$INSTALL_DIR/data"
# Database is owned by the root service.
chown root:root "$INSTALL_DIR/data"
chmod 700 "$INSTALL_DIR/data"
if [ -f "$INSTALL_DIR/data/parental-control.db" ]; then
chown root:root \
"$INSTALL_DIR/data/parental-control.db"
chmod 600 \
"$INSTALL_DIR/data/parental-control.db"
fi
success "Data directory secured."
# ============================================================
# Install systemd service
# ============================================================
info "Installing systemd service"
SERVICE_TEMP="$(mktemp)"
sed \ sed \
"s|%INSTALL_DIR%|$INSTALL_DIR|g" \ "s|%INSTALL_DIR%|$INSTALL_DIR|g" \
"$INSTALL_DIR/parental-control.service" \ "$INSTALL_DIR/parental-control.service" \
> "$SERVICE_FILE" > "$SERVICE_TEMP"
chmod 644 "$SERVICE_FILE" # Ensure the service does not write Python bytecode into the
# Git repository.
systemctl daemon-reload if ! grep -q '^Environment=PYTHONDONTWRITEBYTECODE=' "$SERVICE_TEMP"; then
systemctl enable parental-control.service sed -i \
systemctl restart parental-control.service '/^\[Service\]/a Environment=PYTHONDONTWRITEBYTECODE=1' \
"$SERVICE_TEMP"
fi
echo install \
echo "Systemd service installed and started." -o root \
-g root \
-m 644 \
"$SERVICE_TEMP" \
"$SERVICE_FILE"
# ------------------------------------------ rm -f "$SERVICE_TEMP"
# 5. Install desktop application
# ------------------------------------------
echo success "Systemd service installed."
echo "[5/6] Installing application launcher"
# ============================================================
# Install polkit policy
# ============================================================
info "Checking polkit policy"
if [ -f "$POLKIT_SOURCE" ]; then
mkdir -p /etc/polkit-1/actions
install \
-o root \
-g root \
-m 644 \
"$POLKIT_SOURCE" \
"$POLKIT_DEST"
success "Polkit policy installed."
else
warning "Polkit policy file is not present yet."
warning "Expected: $POLKIT_SOURCE"
warning "Skipping polkit policy installation."
fi
# ============================================================
# Install desktop launcher
# ============================================================
info "Installing desktop launcher"
cat > "$DESKTOP_FILE" <<EOF cat > "$DESKTOP_FILE" <<EOF
[Desktop Entry] [Desktop Entry]
Name=$APP_NAME Name=$APP_NAME
Comment=Linux Parental Control Manager Comment=Linux Parental Control Manager
Exec=$INSTALL_DIR/.venv/bin/python -m desktop.main Exec=$PYTHON -m desktop.main
Path=$INSTALL_DIR Path=$INSTALL_DIR
Icon=preferences-system-parental-controls Icon=preferences-system-parental-controls
Terminal=false Terminal=false
@@ -115,27 +530,231 @@ EOF
chmod 644 "$DESKTOP_FILE" chmod 644 "$DESKTOP_FILE"
echo success "Desktop launcher installed."
echo "Desktop launcher installed:"
echo " $DESKTOP_FILE"
# ------------------------------------------
# 6. Update desktop database
# ------------------------------------------
echo # ============================================================
echo "[6/6] Updating application database" # Validate desktop launcher
# ============================================================
if command -v update-desktop-database >/dev/null 2>&1; then if command -v desktop-file-validate >/dev/null 2>&1; then
update-desktop-database /usr/share/applications
else info "Validating desktop launcher"
echo "update-desktop-database is not installed."
echo "Skipping desktop database update." if ! desktop-file-validate "$DESKTOP_FILE"; then
error "Desktop launcher validation failed."
fi fi
# ------------------------------------------ success "Desktop launcher is valid."
# Verify installation
# ------------------------------------------ else
warning "desktop-file-validate is not available."
fi
# ============================================================
# Update desktop database
# ============================================================
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database \
/usr/share/applications \
>/dev/null 2>&1 || true
fi
# ============================================================
# Reload systemd
# ============================================================
info "Reloading systemd"
systemctl daemon-reload
success "systemd reloaded."
# ============================================================
# Remove stale IPC socket
# ============================================================
info "Cleaning stale IPC socket"
if [ -e "$SOCKET_PATH" ]; then
if [ -S "$SOCKET_PATH" ]; then
rm -f "$SOCKET_PATH"
success "Removed stale IPC socket."
else
warning "$SOCKET_PATH exists but is not a socket."
warning "Leaving it untouched."
fi
fi
# ============================================================
# Enable service
# ============================================================
info "Enabling parental control service"
systemctl enable "$SERVICE_NAME"
success "Service enabled."
# ============================================================
# Restart service
# ============================================================
info "Starting parental control service"
systemctl restart "$SERVICE_NAME"
# ============================================================
# Wait for service
# ============================================================
SERVICE_READY=0
for _ in $(seq 1 15); do
if systemctl is-active --quiet "$SERVICE_NAME"; then
SERVICE_READY=1
break
fi
sleep 1
done
if [ "$SERVICE_READY" -ne 1 ]; then
echo
echo "=========================================="
echo " Service startup failed"
echo "=========================================="
echo
systemctl status \
"$SERVICE_NAME" \
--no-pager || true
echo
echo "Recent service logs:"
echo
journalctl \
-u "$SERVICE_NAME" \
-n 50 \
--no-pager || true
exit 1
fi
success "Service is running."
# ============================================================
# Verify IPC socket
# ============================================================
info "Checking IPC socket"
if [ ! -S "$SOCKET_PATH" ]; then
warning "IPC socket was not created:"
warning "$SOCKET_PATH"
else
success "IPC socket is active."
fi
# ============================================================
# Verify database
# ============================================================
info "Checking database"
if [ -f "$INSTALL_DIR/data/parental-control.db" ]; then
DB_OWNER="$(stat -c '%U:%G' "$INSTALL_DIR/data/parental-control.db")"
DB_MODE="$(stat -c '%a' "$INSTALL_DIR/data/parental-control.db")"
echo " Owner: $DB_OWNER"
echo " Permissions: $DB_MODE"
else
warning "Database does not exist yet."
warning "The service should create it automatically."
fi
# ============================================================
# Final verification
# ============================================================
info "Running final checks"
CHECK_FAILED=0
if systemctl is-active --quiet "$SERVICE_NAME"; then
echo " Service: OK"
else
echo " Service: FAILED"
CHECK_FAILED=1
fi
if [ -x "$PYTHON" ]; then
echo " Python: OK"
else
echo " Python: FAILED"
CHECK_FAILED=1
fi
if [ -f "$DESKTOP_FILE" ]; then
echo " Desktop: OK"
else
echo " Desktop: FAILED"
CHECK_FAILED=1
fi
if [ -S "$SOCKET_PATH" ]; then
echo " IPC: OK"
else
echo " IPC: WARNING"
fi
if [ -f "$POLKIT_DEST" ]; then
echo " Polkit: OK"
else
echo " Polkit: NOT INSTALLED"
fi
if [ "$CHECK_FAILED" -ne 0 ]; then
error "One or more required installation checks failed."
fi
# ============================================================
# Finished
# ============================================================
echo echo
echo "==========================================" echo "=========================================="
@@ -145,11 +764,24 @@ echo
echo "Application:" echo "Application:"
echo " $APP_NAME" echo " $APP_NAME"
echo echo
echo "Launch it from your application menu." echo "Launch:"
echo "On your system:" echo " Application menu"
echo " Rofi"
echo
echo "Rofi:"
echo " Super + D" echo " Super + D"
echo echo
echo "Service:" echo "Service:"
echo " systemctl status parental-control" echo " systemctl status $SERVICE_NAME"
echo
echo "Logs:"
echo " journalctl -u $SERVICE_NAME"
echo
echo "IPC:"
echo " $SOCKET_PATH"
echo
echo "Installation:"
echo " $INSTALL_DIR"
echo echo
echo "==========================================" echo "=========================================="
echo
-3
View File
@@ -1,4 +1 @@
PySide6==6.11.2 PySide6==6.11.2
PySide6_Addons==6.11.2
PySide6_Essentials==6.11.2
shiboken6==6.11.2