winmr-api/Controllers/ProcessController.cs

277 lines
13 KiB
C#
Raw Normal View History

2025-07-02 16:06:50 +03:00
// File: Controllers/ProcessController.cs
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using System.Text.Json.Serialization;
using WebmrAPI.Models;
using WebmrAPI.Services;
2025-07-03 20:01:19 +03:00
using WebmrAPI.Services.Scanners;
2025-07-02 16:06:50 +03:00
namespace WebmrAPI.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public class ProcessController : ControllerBase
{
private readonly ProcessMonitor _monitor;
private readonly ILogger<ProcessController> _logger;
public ProcessController(ProcessMonitor monitor, ILogger<ProcessController> logger)
{
_monitor = monitor;
_logger = logger;
}
private string GetFormattedJson<T>(T data, bool pretty)
{
var options = new JsonSerializerOptions
{
WriteIndented = pretty,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
Converters = { new JsonStringEnumConverter() }
};
return JsonSerializer.Serialize(data, options);
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<ProcessBaseInfo>))]
2025-07-03 20:01:19 +03:00
[ProducesResponseType(StatusCodes.Status400BadRequest)]
2025-07-02 16:06:50 +03:00
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
2025-07-03 20:01:19 +03:00
public IActionResult GetProcesses(
[FromQuery] bool pretty = false,
[FromQuery] string sortBy = "",
[FromQuery] bool desc = false,
[FromQuery] int limit = 0,
[FromQuery] int offset = 0
)
2025-07-02 16:06:50 +03:00
{
try
{
2025-07-03 20:01:19 +03:00
var data = _monitor.GetBufferedProcesses();
if (data != null && !String.IsNullOrEmpty(sortBy))
{
sortBy = sortBy.ToLowerInvariant();
switch (sortBy)
{
case "pid": data = Sort(data, p => p.PID, desc); break;
case "memaddress": data = Sort(data, p => p.MemoryAddress, desc); break;
case "memsize": data = Sort(data, p => p.MemorySize, desc); break;
case "parentpid": data = Sort(data, p => p.ParentPID, desc); break;
case "name": data = Sort(data, p => p.Name, desc); break;
case "filename": data = Sort(data, p => p.FileName, desc); break;
case "commandline": data = Sort(data, p => p.CommandLine, desc); break;
case "threadcount": data = Sort(data, p => p.ThreadCount, desc); break;
case "status": data = Sort(data, p => p.Status, desc); break;
case "starttime": data = Sort(data, p => p.StartTime, desc); break;
case "cpuusage": data = Sort(data, p => p.CpuUsage, desc); break;
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
}
}
return Content(GetFormattedJson(Paginate(data, limit, offset), pretty), "application/json");
2025-07-02 16:06:50 +03:00
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred when getting the list of processes");
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred when receiving processes.");
}
}
[HttpGet("{pid}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ProcessInfo))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
2025-07-03 20:01:19 +03:00
async public Task<IActionResult> GetProcessById(int pid,
[FromQuery] bool pretty = false
)
2025-07-02 16:06:50 +03:00
{
try
{
2025-07-03 20:01:19 +03:00
var data = await _monitor.GetProcessDetails(pid, ScanTarget.ProcessDetails);
2025-07-02 16:06:50 +03:00
if (data == null)
{
return NotFound($"The process with the PID {pid} was not found or its details could not be obtained.");
}
return Content(GetFormattedJson(data, pretty), "application/json");
}
catch (Exception ex)
{
_logger.LogError(ex, $"An error occurred while receiving process details for PID {pid}.");
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving process details for PID {pid}.");
}
}
2025-07-03 20:01:19 +03:00
[HttpGet("{pid}/base_info")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ProcessBaseInfo))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
async public Task<IActionResult> GetProcessBaseInfoById(int pid,
[FromQuery] bool pretty = false
)
{
try
{
var data = await _monitor.GetProcessDetails(pid, 0);
if (data == null)
{
return NotFound($"The process with the PID {pid} was not found or its details could not be obtained.");
}
return Content(GetFormattedJson((ProcessBaseInfo)data, pretty), "application/json");
}
catch (Exception ex)
{
_logger.LogError(ex, $"An error occurred while receiving memory regions for PID {pid}.");
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving memory regions for PID {pid}.");
}
}
[HttpGet("{pid}/memory_regions")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<MemoryRegionInfo>))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
async public Task<IActionResult> GetProcessMemoryPagesById(int pid,
[FromQuery] bool pretty = false,
[FromQuery] string sortBy = "",
[FromQuery] bool desc = false,
[FromQuery] int limit = 0,
[FromQuery] int offset = 0
)
{
try
{
var data = (await _monitor.GetProcessDetails(pid, ScanTarget.MemoryRegions))?.MemoryRegions;
if (data == null)
{
return NotFound($"The process with the PID {pid} was not found or its details could not be obtained.");
}
if (!String.IsNullOrEmpty(sortBy))
{
sortBy = sortBy.ToLowerInvariant();
switch (sortBy)
{
case "state": data = Sort(data, p => p.MemoryState, desc); break;
case "protection": data = Sort(data, p => p.MemoryPageProtection, desc); break;
case "type": data = Sort(data, p => p.MemoryType, desc); break;
case "memaddress": data = Sort(data, p => p.MemoryAddress, desc); break;
case "memsize": data = Sort(data, p => p.MemorySize, desc); break;
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
}
}
return Content(GetFormattedJson(Paginate(data, limit, offset), pretty), "application/json");
}
catch (Exception ex)
{
_logger.LogError(ex, $"An error occurred while receiving memory regions for PID {pid}.");
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving memory regions for PID {pid}.");
}
}
[HttpGet("{pid}/modules")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<MemoryRegionInfo>))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
async public Task<IActionResult> GetProcessModulesById(int pid,
[FromQuery] bool pretty = false,
[FromQuery] string sortBy = "",
[FromQuery] bool desc = false,
[FromQuery] int limit = 0,
[FromQuery] int offset = 0
)
{
try
{
var data = (await _monitor.GetProcessDetails(pid, ScanTarget.Modules))?.Modules;
if (data == null)
{
return NotFound($"The process with the PID {pid} was not found or its details could not be obtained.");
}
if (!String.IsNullOrEmpty(sortBy))
{
sortBy = sortBy.ToLowerInvariant();
switch (sortBy)
{
case "name": data = Sort(data, p => p.ModuleName, desc); break;
case "filename": data = Sort(data, p => p.FileName, desc); break;
case "memaddress": data = Sort(data, p => p.MemoryAddress, desc); break;
case "entryaddress": data = Sort(data, p => p.EntrypointRawAddress, desc); break;
case "memsize": data = Sort(data, p => p.MemorySize, desc); break;
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
}
}
return Content(GetFormattedJson(Paginate(data, limit, offset), pretty), "application/json");
}
catch (Exception ex)
{
_logger.LogError(ex, $"An error occurred while receiving modules for PID {pid}.");
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving modules for PID {pid}.");
}
}
[HttpGet("{pid}/threads")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<MemoryRegionInfo>))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
async public Task<IActionResult> GetProcessThreadsById(int pid,
[FromQuery] bool pretty = false,
[FromQuery] string sortBy = "",
[FromQuery] bool desc = false,
[FromQuery] int limit = 0,
[FromQuery] int offset = 0
)
{
try
{
var data = (await _monitor.GetProcessDetails(pid, ScanTarget.Threads))?.Threads;
if (data == null)
{
return NotFound($"The process with the PID {pid} was not found or its details could not be obtained.");
}
if (!String.IsNullOrEmpty(sortBy))
{
sortBy = sortBy.ToLowerInvariant();
switch (sortBy)
{
case "id": data = Sort(data, p => p.ID, desc); break;
case "priority": data = Sort(data, p => p.CurrentPriority, desc); break;
case "basepriority": data = Sort(data, p => p.BasePriority, desc); break;
case "cpuusage": data = Sort(data, p => p.CpuUsage, desc); break;
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
}
}
return Content(GetFormattedJson(Paginate(data, limit, offset), pretty), "application/json");
}
catch (Exception ex)
{
_logger.LogError(ex, $"An error occurred while receiving threads for PID {pid}.");
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving threads for PID {pid}.");
}
}
private static IEnumerable<T1> Sort<T1, T2>(IEnumerable<T1> data, Func<T1, T2> selector, bool desc)
{
return desc ? data.OrderByDescending(selector) : data.OrderBy(selector);
}
private IEnumerable<T> Paginate<T>(IEnumerable<T> data, int limit, int offset)
{
if (offset > 0) data = data.Skip(offset);
if (limit > 0) data = data.Take(limit);
return data;
}
2025-07-02 16:06:50 +03:00
}
}