본문 바로가기

자동화

[202407] 시스템 프로세스 및 서비스 모니터링 및 관리 프로그램

반응형

시스템 프로세스 및 서비스 모니터링 및 관리 프로그램

소개

이번 포스트에서는 시스템의 프로세스와 서비스를 모니터링하고, 지정된 자원 사용량을 초과하는 경우 해당 프로세스 또는 서비스를 종료하는 Python 스크립트를 소개합니다. 이 스크립트는 WMI, psutil, win32serviceutil 등의 라이브러리를 사용하여 시스템 정보를 수집하고 관리합니다. 또한, GUI를 통한 사용자 인증과 로그 파일을 통한 기록을 포함하고 있습니다.

주요 기능

이 스크립트는 다음과 같은 주요 기능을 갖추고 있습니다:

  1. 관리자 권한 확인 및 요청
  2. 프로세스와 서비스의 CPU 및 메모리 사용량 모니터링
  3. 지정된 임계값을 초과하는 경우 프로세스 또는 서비스 종료
  4. 로그 파일을 통한 활동 기록
  5. 웹 스크래핑을 통한 패스워드 검증

세부 설명

1. 관리자 권한 확인 및 요청

스크립트는 시스템 자원을 관리하기 위해 관리자 권한이 필요합니다. ctypes 라이브러리를 사용하여 현재 사용자가 관리자 권한을 가지고 있는지 확인하고, 관리자 권한이 없는 경우 관리자 권한으로 스크립트를 재실행합니다.

2. 프로세스와 서비스의 CPU 및 메모리 사용량 모니터링

psutil 라이브러리를 사용하여 시스템의 모든 프로세스를 순회하면서 각 프로세스의 CPU 및 메모리 사용량을 수집합니다. wmi 라이브러리를 통해 각 프로세스가 서비스인지 확인하고, 서비스인 경우 해당 서비스의 이름을 가져옵니다.

3. 지정된 임계값을 초과하는 경우 프로세스 또는 서비스 종료

사용자가 설정한 CPU 및 메모리 사용량 임계값을 초과하는 프로세스나 서비스가 발견되면, win32serviceutil 라이브러리를 사용하여 해당 서비스를 종료하거나, psutil 라이브러리를 사용하여 프로세스를 종료합니다.

4. 로그 파일을 통한 활동 기록

logging 라이브러리를 사용하여 모니터링 과정에서 발생하는 모든 활동을 로그 파일에 기록합니다. 로그 파일은 사용자가 쉽게 접근할 수 있는 경로에 저장되며, 모니터링 사이클마다 총 자원 사용량을 기록하여 시스템 상태를 추적할 수 있도록 합니다.

5. 웹 스크래핑을 통한 패스워드 검증

requestsBeautifulSoup 라이브러리를 사용하여 지정된 웹페이지에서 패스워드를 스크래핑합니다. 사용자는 스크립트 실행 시 패스워드를 입력해야 하며, 입력한 패스워드가 스크래핑된 패스워드와 일치하는 경우에만 모니터링이 시작됩니다.

import psutil
import time
import wmi
import win32serviceutil
import os
import sys
import ctypes
from datetime import datetime
import logging
import getpass
import requests
from bs4 import BeautifulSoup
from tkinter import simpledialog, messagebox

# WMI 초기화
c = wmi.WMI()

# 로그 파일 설정 (쓰기 권한이 있는 경로로 변경)
log_file_path = os.path.expanduser('~\\process_service_monitor.log')
logging.basicConfig(filename=log_file_path,
                    level=logging.INFO,
                    format='%(asctime)s - %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    filemode='a')  # 'a' 모드는 파일에 내용을 추가함
# 로그 파일 경로 출력
print(f"Log file is being saved to: {log_file_path}")

# 로거 설정
logger = logging.getLogger()
handler = logging.FileHandler(log_file_path, mode='a')
formatter = logging.Formatter('%(asctime)s - %(message)s', '%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# 버퍼링을 비활성화하기 위해 핸들러의 스트림 플러시 설정
handler.stream.flush = sys.stdout.flush

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def get_power_usage(pid):
    # PID를 기반으로 전력 사용량 정보를 가져오는 함수
    process = psutil.Process(pid)
    cpu_usage = process.cpu_percent(interval=1)  # CPU 사용량
    memory_info = process.memory_info()
    memory_usage = memory_info.rss / (1024 * 1024)  # 메모리 사용량 (MB 단위)
    return cpu_usage, memory_usage

def get_power_usage(pid):
    # PID를 기반으로 전력 사용량 정보를 가져오는 함수
    process = psutil.Process(pid)
    cpu_usage = process.cpu_percent(interval=None)  # 현재 순간의 CPU 사용량
    memory_info = process.memory_info()
    memory_usage = memory_info.rss / (1024 * 1024)  # 메모리 사용량 (MB 단위)
    return cpu_usage, memory_usage

def monitor_and_terminate_services(cpu_threshold, memory_threshold):
    while True:
        total_cpu_usage = 0.0
        total_memory_usage = 0.0

        for proc in psutil.process_iter(['pid', 'name']):
            try:
                pid = proc.info['pid']
                name = proc.info['name']

                # 필터링: PID 0 (System Idle Process) 및 기타 시스템 프로세스 제외
                if pid == 0 or name.lower() == 'system idle process':
                    continue

                cpu_usage, memory_usage = get_power_usage(pid)

                # 누적 자원 사용량 계산
                total_cpu_usage += cpu_usage
                total_memory_usage += memory_usage

                # 현재 날짜와 시간 가져오기
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

                # 서비스인지 프로세스인지 구분
                service_name = None
                for service in c.Win32_Service(ProcessId=pid):
                    service_name = service.DisplayName  # 서비스의 전체 이름을 사용
                    break

                if service_name:
                    log_message = f"{current_time} Checked service {service_name} (Process {name}, PID: {pid}) - CPU Usage: {cpu_usage}%, Memory Usage: {memory_usage} MB"
                else:
                    log_message = f"{current_time} Checked process {name} (PID: {pid}) - CPU Usage: {cpu_usage}%, Memory Usage: {memory_usage} MB"

                # 로그 파일에 메시지 기록
                logger.info(log_message)
                print(log_message)

                if cpu_usage > cpu_threshold or memory_usage > memory_threshold:
                    if service_name:
                        try:
                            log_message = f"Stopping service {service_name} (Process {name}, PID: {pid}) - CPU Usage: {cpu_usage}%, Memory Usage: {memory_usage} MB"
                            win32serviceutil.StopService(service_name)
                        except Exception as e:
                            log_message = f"Failed to stop service {service_name}: {e}"
                        logger.info(log_message)
                        print(log_message)
                    else:
                        try:
                            log_message = f"Terminating process {name} (PID: {pid}) - CPU Usage: {cpu_usage}%, Memory Usage: {memory_usage} MB"
                            p = psutil.Process(pid)
                            p.terminate()
                        except Exception as e:
                            log_message = f"Failed to terminate process {name} (PID: {pid}): {e}"
                        logger.info(log_message)
                        print(log_message)
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e:
                log_message = f"Failed to process {proc.info['name']} (PID: {proc.info['pid']}): {e}"
                logger.info(log_message)
                print(log_message)

        # 한 사이클 완료 후 총 자원 사용량 로그 기록
        log_message = f"Total CPU Usage: {total_cpu_usage}%, Total Memory Usage: {total_memory_usage} MB"
        logger.info(log_message)
        print(log_message)

        log_message = "Completed one monitoring cycle"
        logger.info(log_message)
        print(log_message)
        time.sleep(5)  # 5초마다 체크

# 패스워드 스크랩
def pwd_scrap():
    url = "https://khc-developer.tistory.com/79"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
    }
    try:
        response = requests.get(url, headers=headers, verify=False)  # SSL 인증서 검증 무시
        response.raise_for_status()  # Raise an error for bad status codes

        soup = BeautifulSoup(response.text, 'html.parser')
        password_element = soup.select_one(".tt_article_useless_p_margin.contents_style p[data-ke-size='size16']")
        if password_element:
            return password_element.get_text().strip()
        else:
            raise ValueError("Password element not found on the webpage.")
    except Exception as e:
        messagebox.showerror("Error", f"Failed to retrieve password from the web: {e}")
        return None        

def main():
    password = pwd_scrap()
    while True:
        user_password = getpass.getpass("Enter the password: ")

        if user_password == password:
            print("Password is correct. Starting monitoring...")
            cpu_threshold = 80  # CPU 사용량이 80% 이상일 경우
            while True:
                try:
                    memory_threshold = float(input("Enter the memory threshold (in MB) * 일반 300: "))
                    break
                except ValueError:
                    print("Invalid input. Please enter a number.")
            # total_cpu_usage, total_memory_usage = get_total_usage()
            # log_message = f"Initial total CPU Usage: {total_cpu_usage}%, Initial total Memory Usage: {total_memory_usage} MB"
            # logger.info(log_message)
            # print(log_message)
            monitor_and_terminate_services(cpu_threshold, memory_threshold)
            break
        else:
            print("Incorrect password. Please try again.")

if __name__ == "__main__":
    if not is_admin():
        # 관리자 권한으로 스크립트 재실행
        ctypes.windll.shell32.ShellExecuteW(
            None, "runas", sys.executable, ' '.join(sys.argv), None, 1)
    else:
        main()

사용법

스크립트 실행

스크립트를 실행하면 패스워드를 입력하라는 프롬프트가 나타납니다. 올바른 패스워드를 입력해야 모니터링이 시작됩니다.
모니터링이 시작되면 스크립트는 주기적으로 모든 프로세스와 서비스를 체크하여 CPU 및 메모리 사용량을 로그 파일에 기록합니다.
지정된 임계값을 초과하는 프로세스나 서비스가 발견되면 해당 프로세스나 서비스를 종료하고, 이 활동을 로그 파일에 기록합니다.

사용자 설정

  • CPU 임계값: 기본값은 80%로 설정되어 있습니다. 사용자는 이를 변경할 수 있습니다.
  • 메모리 임계값: 사용자 입력을 통해 설정됩니다. 일반적으로 300MB 이상을 권장합니다.

결론

이 스크립트는 시스템 자원을 효율적으로 관리하고, 과도한 자원 사용으로 인한 시스템 성능 저하를 방지하는 데 유용합니다. 관리자 권한 확인, 패스워드 검증, 상세한 로그 기록 등의 기능을 통해 안정적이고 신뢰성 있는 시스템 모니터링을 제공합니다. 이를 통해 반복적인 관리 작업을 자동화하고, 시스템 상태를 실시간으로 추적할 수 있습니다.

이 스크립트를 사용하여 여러분의 시스템 관리 작업을 더욱 효율적이고 체계적으로 만들어보세요!

반응형