/*
    Renice process tree. Root privileges needed. Use ps -AH for show tree.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on C (translated from FreePascal).
    Copyright (C) 2025-2026 Artyomov Alexander
    Used https://chatgpt.com/, https://chat.deepseek.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/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include <sys/resource.h>
#include <errno.h>

typedef struct PidNode {
    long pid;
    struct PidNode* next;
} PidNode;

typedef struct {
    PidNode* head;
    PidNode* tail;
    int count;
} PidList;

void initPidList(PidList* list) {
    list->head = NULL;
    list->tail = NULL;
    list->count = 0;
}

void addPidToList(PidList* list, long pid) {
    PidNode* newNode = (PidNode*)malloc(sizeof(PidNode));
    if (!newNode) return;
    newNode->pid = pid;
    newNode->next = NULL;
    
    if (list->tail) {
        list->tail->next = newNode;
        list->tail = newNode;
    } else {
        list->head = list->tail = newNode;
    }
    list->count++;
}

void freePidList(PidList* list) {
    PidNode* current = list->head;
    while (current) {
        PidNode* temp = current;
        current = current->next;
        free(temp);
    }
    list->head = list->tail = NULL;
    list->count = 0;
}

long getPidFromList(PidList* list, int index) {
    if (index < 0 || index >= list->count) return -1;
    PidNode* current = list->head;
    for (int i = 0; i < index; i++) {
        current = current->next;
    }
    return current->pid;
}

PidList* listChildPids(long pid) {
    PidList* result = (PidList*)malloc(sizeof(PidList));
    if (!result) return NULL;
    initPidList(result);
    
    DIR* procDir = opendir("/proc");
    if (!procDir) return result;
    
    struct dirent* entry;
    while ((entry = readdir(procDir)) != NULL) {
        // Проверяем, что имя директории состоит только из цифр (PID)
        int isNumber = 1;
        for (char* p = entry->d_name; *p; p++) {
            if (*p < '0' || *p > '9') {
                isNumber = 0;
                break;
            }
        }
        if (!isNumber) continue;
        
        char statPath[PATH_MAX];
        int written = snprintf(statPath, sizeof(statPath), "/proc/%s/stat", entry->d_name);
        if (written < 0 || written >= (int)sizeof(statPath)) {
            // Путь слишком длинный (не должно случиться, но проверим)
            continue;
        }
        
        FILE* statFile = fopen(statPath, "r");
        if (!statFile) continue;
        
        char buffer[1024];
        if (fgets(buffer, sizeof(buffer), statFile)) {
            // Формат /proc/PID/stat: pid (comm) state ppid ...
            char* closeParen = strrchr(buffer, ')');
            if (closeParen) {
                char* afterComm = closeParen + 2; // Пропускаем ') '
                long ppid;
                if (sscanf(afterComm, "%*c %ld", &ppid) == 1) {
                    if (ppid == pid) {
                        long childPid = atol(entry->d_name);
                        if (childPid > 0) {
                            addPidToList(result, childPid);
                        }
                    }
                }
            }
        }
        fclose(statFile);
    }
    closedir(procDir);
    return result;
}

void reniceProcessTree(long pid) {
    // Сначала обрабатываем потомков
    PidList* children = listChildPids(pid);
    if (children) {
        for (int i = 0; i < children->count; i++) {
            long childPid = getPidFromList(children, i);
            if (childPid > 0) {
                reniceProcessTree(childPid);
            }
        }
        freePidList(children);
        free(children);
    }
    
    // Затем сам процесс
    if (setpriority(PRIO_PROCESS, pid, -20) != 0) {
        fprintf(stderr, "Не удалось установить приоритет для PID %ld: %s\n", 
                pid, strerror(errno));
    } else {
        printf("Установлен приоритет -20 для PID %ld\n", pid);
    }
}

int main(int argc, char* argv[]) {
    if (argc >= 2) {
        long targetPid = atol(argv[1]);
        if (targetPid <= 0) {
            fprintf(stderr, "Укажите корректный PID\n");
            return 1;
        }
        reniceProcessTree(targetPid);
    } else {
        fprintf(stderr, "Usage: %s <PID>\n", argv[0]);
        return 1;
    }
    return 0;
}