program renice_tree;

{
    Renice process tree. Root privileges needed. Use ps -AH for show tree.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2025  Artyomov Alexander
    Used https://chatgpt.com/
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

{$MODE OBJFPC}{$H+}
{$linklib c}

uses
  BaseUnix, Unix, SysUtils, Classes;

function ListChildPIDs(pid: LongInt): TList;
var
  dir: TSearchRec;
  ppidStr, statFile: string;
  statF: Text;
  childPID, ppid: LongInt;
  parts: TStringList;
begin
  result := TList.Create;
  if FindFirst('/proc/*', faDirectory, dir) = 0 then
  begin
    repeat
      if (dir.Name[1] in ['0'..'9']) then
      begin
        statFile := '/proc/' + dir.Name + '/stat';
        try
          Assign(statF, statFile);
          Reset(statF);
          ReadLn(statF, ppidStr);
          Close(statF);

          parts := TStringList.Create;
          parts.Delimiter := ' ';
          parts.DelimitedText := ppidStr;
          if parts.Count >= 4 then
          begin
            ppid := StrToIntDef(parts[3], -1);
            if ppid = pid then
            begin
              childPID := StrToIntDef(dir.Name, -1);
              if childPID > 0 then
                result.Add(Pointer(childPID));
            end;
          end;
          parts.Free;
        except
          // Игнорируем ошибки, например, если процесс исчез
        end;
      end;
    until FindNext(dir) <> 0;
    FindClose(dir);
  end;
end;

procedure ReniceProcessTree(pid: LongInt);
var
  children: TList;
  i: Integer;
  childPID: LongInt;
begin
  // Сначала обработаем потомков
  children := ListChildPIDs(pid);
  for i := 0 to children.Count - 1 do
  begin
    childPID := LongInt(children[i]);
    ReniceProcessTree(childPID);
  end;
  // Затем сам процесс
  if fpSetPriority(PRIO_PROCESS, pid, -20) <> 0 then
    WriteLn('Не удалось установить приоритет для PID ', pid, ': ', fpGetErrno)
  else
    WriteLn('Установлен приоритет -20 для PID ', pid);
  children.Free;
end;

var
  targetPID: LongInt;
begin
if ParamCount >= 1 then begin
  targetPID := StrToIntDef(ParamStr(1), -1);
  if targetPID <= 0 then begin
    WriteLn('Укажите корректный PID');
    Halt(1);
  end else ReniceProcessTree(targetPID);
end;
end.