#!/usr/bin/env python3 import os import subprocess import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk # Kurulum bayrağının konumu: Kullanıcının ana dizininde, yazma izni sorunu yaşamamak için. SETUP_FLAG_FILE = os.path.expanduser("~/.setup_tamam") # -------------------- Yardımcı Fonksiyonlar -------------------- def run_as_root(cmd_list): """ Belirtilen komut listesini pkexec kullanarak root ayrıcalıklarıyla çalıştırır. Eğer kullanıcı root şifresini girmeyi iptal ederse, uygun uyarı mesajı verir. """ try: subprocess.run(["pkexec"] + cmd_list, check=True) except subprocess.CalledProcessError as e: if e.returncode == 1: raise RuntimeError("Root yetkilendirmesi iptal edildi! Lütfen root şifresini giriniz. Sistem, root yetkisi ile çalıştırılmalıdır.") raise RuntimeError(f"Komut hatası: {e.cmd}, Çıkış kodu: {e.returncode}") def set_setup_complete_flag(): """ Kurulumun tamamlandığını belirten bayrak dosyasını oluşturur. Böylece sonraki çalıştırmalarda kurulum sihirbazı tekrarlanmaz. """ try: with open(SETUP_FLAG_FILE, "w") as file: file.write("setup_complete") except Exception as ex: print(f"Kurulum bayrağı ayarlanırken hata: {ex}") def read_users(): """ Sistemdeki mevcut kullanıcıları /etc/passwd dosyasından okur. """ users = [] try: with open("/etc/passwd", "r") as file: for line in file: parts = line.split(":") if parts: users.append(parts[0]) except Exception as ex: print(f"Kullanıcılar okunurken hata: {ex}") return users def get_mount_device(mount_point): """ Belirtilen mount noktasının bağlı olduğu aygıtı /proc/mounts dosyasından bulur. """ try: with open("/proc/mounts", "r") as file: for line in file: parts = line.split() if parts[1] == mount_point: return parts[0] except Exception: pass return None def overlayroot_status(): """ Sistem Dondurma durumunu, /media/root-rw montajı ve /etc/overlayroot.conf dosyasına göre belirler. """ if get_mount_device("/media/root-rw"): return True conf_path = "/etc/overlayroot.conf" if os.path.exists(conf_path): try: with open(conf_path, "r") as file: content = file.read() return "tmpfs" in content except Exception: return False else: try: result = subprocess.run(["dpkg", "-l", "romtal-sistem-dondurma"], capture_output=True, text=True) return "overlayroot" in result.stdout except Exception: return False # -------------------- Kurulum Sihirbazı (Setup Wizard) -------------------- class SetupWizard(Gtk.Window): """ Üç aşamalı kurulum sihirbazı: 1. Açıklama Sayfası: Neden non‑root kullanıcı kullanılmalı? İki seçenek sunar. 2. Kullanıcı Oluşturma Sayfası: Önerilen "ogrenci" adında, sınırlı yetkili kullanıcı oluşturulur. 3. Onay Sayfası: İşlemin tamamlandığı bildirildiği sayfa. NOT: Notebook sekmeleri gizlenmiştir; kullanıcı yalnızca sayfa içindeki gezinme butonlarıyla ilerler. """ def __init__(self): super().__init__(title="Kurulum Sihirbazı") self.set_default_size(600, 500) self.set_border_width(20) self.notebook = Gtk.Notebook() self.notebook.set_show_tabs(False) self.add(self.notebook) self.create_explanation_page() self.create_user_creation_page() self.create_confirmation_page() def create_explanation_page(self): page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) page.set_border_width(15) explanation_label = Gtk.Label() explanation_label.set_markup( "R.O.M.T.A.L | https://rasimoneltml.meb.k12.tr/\n\n" "Neden Yeni Bir Kullanıcı Oluşturmalısınız?\n\n" "Bu sistem, güvenlik açısından root yetkisinin kullanılmaması gereken bir yapıya sahiptir. " "Root yetkisine sahip bir kullanıcı, sistem üzerinde tam kontrol sağlar ve güvenlik risklerini artırır.\n\n" "Eğer sisteminizde zaten root yetkisine sahip olmayan bir kullanıcı mevcutsa, bu adımı atlayabilirsiniz. " "Aksi halde, lütfen 'Devam Et' butonuna basarak örneğin 'ogrenci' adlı sınırlı yetkili bir kullanıcı oluşturun." ) explanation_label.set_line_wrap(True) page.pack_start(explanation_label, True, True, 0) nav_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) skip_button = Gtk.Button(label="Atla") continue_button = Gtk.Button(label="Devam Et") skip_button.connect("clicked", self.on_skip_clicked) continue_button.connect("clicked", self.on_continue_to_creation) nav_box.pack_start(skip_button, True, True, 0) nav_box.pack_start(continue_button, True, True, 0) page.pack_start(nav_box, False, False, 0) self.notebook.append_page(page, Gtk.Label(label="Açıklama")) def create_user_creation_page(self): page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) page.set_border_width(15) instruction_label = Gtk.Label( label="Yeni bir kullanıcı oluşturmak için lütfen bilgileri giriniz. " "Önerilen kullanıcı adı: 'ogrenci'" ) instruction_label.set_line_wrap(True) page.pack_start(instruction_label, False, False, 0) form_grid = Gtk.Grid(column_spacing=10, row_spacing=10) username_label = Gtk.Label(label="Kullanıcı Adı:") self.username_entry = Gtk.Entry() self.username_entry.set_text("ogrenci") self.username_entry.set_placeholder_text("örn: ogrenci") password_label = Gtk.Label(label="Şifre:") self.password_entry = Gtk.Entry() self.password_entry.set_placeholder_text("Güçlü bir şifre giriniz") self.password_entry.set_visibility(False) form_grid.attach(username_label, 0, 0, 1, 1) form_grid.attach(self.username_entry, 1, 0, 1, 1) form_grid.attach(password_label, 0, 1, 1, 1) form_grid.attach(self.password_entry, 1, 1, 1, 1) page.pack_start(form_grid, False, False, 0) nav_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) back_button = Gtk.Button(label="Geri") create_button = Gtk.Button(label="Kullanıcı Oluştur") back_button.connect("clicked", lambda w: self.notebook.set_current_page(0)) create_button.connect("clicked", self.on_create_user) nav_box.pack_start(back_button, True, True, 0) nav_box.pack_start(create_button, True, True, 0) page.pack_start(nav_box, False, False, 0) self.notebook.append_page(page, Gtk.Label(label="Kullanıcı Oluşturma")) def create_confirmation_page(self): page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) page.set_border_width(15) confirmation_label = Gtk.Label( label="Kullanıcı başarıyla oluşturuldu.\nKurulum tamamlandı.\n" "Ana arayüze geçmek için 'Devam Et' butonuna basınız." ) confirmation_label.set_line_wrap(True) page.pack_start(confirmation_label, True, True, 0) nav_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) back_button = Gtk.Button(label="Geri") continue_button = Gtk.Button(label="Devam Et") back_button.connect("clicked", lambda w: self.notebook.set_current_page(1)) continue_button.connect("clicked", self.on_final_continue) nav_box.pack_start(back_button, True, True, 0) nav_box.pack_start(continue_button, True, True, 0) page.pack_start(nav_box, False, False, 0) self.notebook.append_page(page, Gtk.Label(label="Onay")) def on_skip_clicked(self, widget): """ Eğer sisteminizde root yetkisine sahip olmayan bir kullanıcı mevcutsa, bu adımı atlayarak ana arayüze geçin. """ set_setup_complete_flag() self.hide() main_app = SistemDondurmaGUI() main_app.connect("destroy", Gtk.main_quit) main_app.show_all() def on_continue_to_creation(self, widget): """ 'Devam Et' butonuna basıldığında, kullanıcı oluşturma sayfasına geçiş yapılır. """ self.notebook.set_current_page(1) def on_create_user(self, widget): """ Kullanıcı adı ve şifre bilgilerini alır, ardından önerilen non‑root kullanıcıyı oluşturur. Eğer girilen kullanıcı adı zaten sistemde mevcutsa hata mesajı gösterip farklı bir isim istenir. Kullanıcı oluşturma ve şifre ayarlama işlemleri tek bir pkexec çağrısında birleştirilmiştir. """ username = self.username_entry.get_text().strip() password = self.password_entry.get_text().strip() if not username or not password: self.show_dialog("Kullanıcı adı ve şifre boş bırakılamaz!", True) return if username in read_users(): self.show_dialog(f"{username} adlı kullanıcı zaten mevcut! Lütfen farklı bir kullanıcı adı giriniz.", True) return try: cmd = ["sh", "-c", f"useradd -m --shell /bin/bash {username} && echo '{username}:{password}' | chpasswd"] run_as_root(cmd) self.show_dialog(f"{username} adlı kullanıcı başarıyla oluşturuldu.", False) self.notebook.set_current_page(2) except Exception as e: self.show_dialog(f"Kullanıcı oluşturulurken hata: {e}", True) def on_final_continue(self, widget): """ Kurulum tamamlandığında bayrak dosyası oluşturulur ve ana arayüz açılır. """ set_setup_complete_flag() self.hide() main_app = SistemDondurmaGUI() main_app.connect("destroy", Gtk.main_quit) main_app.show_all() def show_dialog(self, message, is_error=False): dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR if is_error else Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text=message ) dialog.run() dialog.destroy() # -------------------- Ana Uygulama Arayüzü (Sistem Dondurma GUI) -------------------- class SistemDondurmaGUI(Gtk.Window): def __init__(self): super().__init__(title="R.O.M.T.A.L Sistem Dondurma") self.set_default_size(500, 300) self.set_border_width(20) self.setup_css() self.overlayroot_aktif = overlayroot_status() self.create_ui() def setup_css(self): css = b""" label { font-size: 14px; } """ style_provider = Gtk.CssProvider() style_provider.load_from_data(css) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) def create_ui(self): grid = Gtk.Grid(column_spacing=10, row_spacing=10) grid.set_column_homogeneous(True) self.status_label = Gtk.Label() self.enable_btn = Gtk.Button(label="Sistem Dondurma Aktif Et") self.enable_btn.connect("clicked", self.enable_overlayroot) self.disable_btn = Gtk.Button(label="Sistem Dondurma Devre Dışı") self.disable_btn.connect("clicked", self.disable_overlayroot) footer_label = Gtk.Label(label="R.O.M.T.A.L | https://rasimoneltml.meb.k12.tr/") grid.attach(self.status_label, 0, 0, 2, 1) grid.attach(self.enable_btn, 0, 1, 1, 1) grid.attach(self.disable_btn, 1, 1, 1, 1) grid.attach(footer_label, 0, 2, 2, 1) self.add(grid) self.update_status() def update_status(self): status_text = "Aktif" if self.overlayroot_aktif else "Devre Dışı" self.status_label.set_markup(f"Sistem Dondurma Durumu: {status_text}") self.enable_btn.set_sensitive(not self.overlayroot_aktif) self.disable_btn.set_sensitive(self.overlayroot_aktif) def show_dialog(self, message, is_error=False): dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR if is_error else Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text=message ) dialog.run() dialog.destroy() def enable_overlayroot(self, widget): try: cmd = [ "sh", "-c", ("rm -f /etc/overlayroot.conf && " "echo 'overlayroot_cfgdisk=\"disable\"' > /etc/overlayroot.conf && " "echo 'overlayroot=\"tmpfs\"' >> /etc/overlayroot.conf") ] run_as_root(cmd) self.overlayroot_aktif = True self.update_status() self.show_dialog("Sistem Dondurma başarıyla aktif edildi.\nDeğişikliklerin geçerli olması için sistemi yeniden başlatın.") except Exception as e: self.show_dialog(f"Sistem Dondurma etkinleştirilirken hata: {e}", True) def disable_overlayroot(self, widget): """ Sistem Dondurma işlemini devre dışı bırakır. Eğer /media/root-ro mount edilmişse ilgili dizin üzerinden işlem yapılır; aksi halde, /etc/overlayroot.conf dosyası doğrudan güncellenir. """ try: device = get_mount_device("/media/root-ro") if device: cmd = [ "sh", "-c", (f"mount -o remount,rw {device} && " "rm -f /media/root-ro/etc/overlayroot.conf && " "echo 'overlayroot_cfgdisk=\"disable\"' > /media/root-ro/etc/overlayroot.conf && " "echo 'overlayroot=\"\"' >> /media/root-ro/etc/overlayroot.conf") ] else: cmd = [ "sh", "-c", ("rm -f /etc/overlayroot.conf && " "echo 'overlayroot_cfgdisk=\"disable\"' > /etc/overlayroot.conf && " "echo 'overlayroot=\"\"' >> /etc/overlayroot.conf") ] run_as_root(cmd) self.overlayroot_aktif = False self.update_status() self.show_dialog("Sistem Dondurma başarıyla devre dışı bırakıldı.\nDeğişikliklerin geçerli olması için sistemi yeniden başlatın.") except Exception as e: self.show_dialog(f"Sistem Dondurma devre dışı bırakılırken hata: {e}", True) # -------------------- Uygulama Giriş Noktası -------------------- if __name__ == "__main__": if not os.path.exists(SETUP_FLAG_FILE): wizard = SetupWizard() wizard.connect("destroy", Gtk.main_quit) wizard.show_all() else: app = SistemDondurmaGUI() app.connect("destroy", Gtk.main_quit) app.show_all() Gtk.main()