|
@@ -0,0 +1,416 @@
|
|
|
+#define _POSIX_C_SOURCE 200809L
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <dirent.h>
|
|
|
+#include <string.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <pwd.h>
|
|
|
+#include <time.h>
|
|
|
+#include "error.h"
|
|
|
+#include "color.h"
|
|
|
+#include "constante.h"
|
|
|
+
|
|
|
+#define MAX_CMD_SIZE 50
|
|
|
+
|
|
|
+typedef long long int llint;
|
|
|
+typedef unsigned long long ull;
|
|
|
+
|
|
|
+typedef struct{
|
|
|
+ char* path; //Chemin vers le dossier du processus /./proc/[pid]/
|
|
|
+ int pid; //Le pid
|
|
|
+ int uid; //Le uid du proprietaire
|
|
|
+ char state; //L'etat
|
|
|
+ char* cmd; //La commande
|
|
|
+ llint tty;
|
|
|
+ llint cpu;
|
|
|
+ ull start;
|
|
|
+ llint vsz;
|
|
|
+ llint rss;
|
|
|
+}processus;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Indique si une string est un nombre
|
|
|
+ * @param char* Le string
|
|
|
+ * @return Vrai/Faux
|
|
|
+ */
|
|
|
+boolean is_numeric(const char* str){
|
|
|
+ while(*str){
|
|
|
+ if(*str < '0' || *str > '9'){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ str++;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Tri les processus par pid croissant
|
|
|
+ * @param processus** Le tableau de processus
|
|
|
+ */
|
|
|
+void sort_pid(processus** proc, int size){
|
|
|
+ processus* tmp;
|
|
|
+ int index;
|
|
|
+ for(int i = 1; i < size; i++){
|
|
|
+ index = i;
|
|
|
+ while(index > 0 && proc[index]->pid < proc[index - 1]->pid){
|
|
|
+ tmp = proc[index];
|
|
|
+ proc[index] = proc[index - 1];
|
|
|
+ proc[index - 1] = tmp;
|
|
|
+ index--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int get_uptime() {
|
|
|
+ FILE* procUptime;
|
|
|
+ int sec, ssec;
|
|
|
+ procUptime = fopen("/proc/uptime", "r");
|
|
|
+ int tmp = fscanf(procUptime, "%d.%ds", &sec, &ssec);
|
|
|
+ tmp++; //Pour ne pas avoir de warning
|
|
|
+ fclose(procUptime);
|
|
|
+ return (sec * sysconf(_SC_CLK_TCK)) + ssec;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Liste les processus actifs
|
|
|
+ * @param int* Le nombre de processus actif
|
|
|
+ * @return processus** La liste
|
|
|
+ */
|
|
|
+processus** list_process(int* nb){
|
|
|
+ struct dirent** dir;
|
|
|
+ struct stat info;
|
|
|
+ int nbRes;
|
|
|
+ int length, compteur = 0;
|
|
|
+ char* path;
|
|
|
+ processus** result;
|
|
|
+ //Recup les dossiers
|
|
|
+ if((nbRes = scandir("/./proc/", &dir, 0, alphasort)) == ERR){
|
|
|
+ addperror("Impossible de scanner le dossier");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ //Compte le nombre de resultat
|
|
|
+ for(int i = 0; i < nbRes; i++){
|
|
|
+ //Création chemin
|
|
|
+ length = strlen(dir[i]->d_name) + 10;
|
|
|
+ path = malloc(sizeof(char) * length);
|
|
|
+ memset(path, 0, length);
|
|
|
+ snprintf(path, length, "/./proc/%s/", dir[i]->d_name);
|
|
|
+ //Recup info
|
|
|
+ if(stat(path, &info) == ERR){
|
|
|
+ //Pas un dossier
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //Ne regarde que les dossiers dont le nom est un nombre
|
|
|
+ if(S_ISDIR(info.st_mode) && is_numeric(dir[i]->d_name)){
|
|
|
+ compteur++;
|
|
|
+ }
|
|
|
+ free(path);
|
|
|
+ }
|
|
|
+ //Allocation resultat
|
|
|
+ result = malloc(sizeof(processus*) * compteur);
|
|
|
+ //Ajout des resultats
|
|
|
+ compteur = 0;
|
|
|
+ for(int i = 0; i < nbRes; i++){
|
|
|
+ //Création chemin
|
|
|
+ length = strlen(dir[i]->d_name) + 10;
|
|
|
+ path = malloc(sizeof(char) * length);
|
|
|
+ memset(path, 0, length);
|
|
|
+ snprintf(path, length, "/./proc/%s/", dir[i]->d_name);
|
|
|
+ //Recup info
|
|
|
+ if(stat(path, &info) == ERR){
|
|
|
+ //Pas un dossier
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //Ne regarde que les dossiers dont le nom est un nombre
|
|
|
+ if(S_ISDIR(info.st_mode) && is_numeric(dir[i]->d_name)){
|
|
|
+ result[compteur] = malloc(sizeof(processus));
|
|
|
+ result[compteur]->path = path;
|
|
|
+ result[compteur]->pid = atoi(dir[i]->d_name);
|
|
|
+ result[compteur]->uid = info.st_uid;
|
|
|
+ compteur++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ free(path);
|
|
|
+ }
|
|
|
+ //Libere memoire
|
|
|
+ while(nbRes--){
|
|
|
+ free(dir[nbRes]);
|
|
|
+ }
|
|
|
+ free(dir);
|
|
|
+ //Retour
|
|
|
+ if(nb != NULL){
|
|
|
+ *nb = compteur;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Lecture du fichier status d'un processus
|
|
|
+ * @param processus* Le processus
|
|
|
+ * @return Reussite
|
|
|
+ */
|
|
|
+boolean read_status(processus* proc){
|
|
|
+ char* file;
|
|
|
+ FILE* f;
|
|
|
+ int length, tmp;
|
|
|
+ char buffer[BUFFER_SIZE];
|
|
|
+ llint trash;
|
|
|
+ //Recup nom du fichier
|
|
|
+ length = strlen(proc->path) + 5;
|
|
|
+ file = malloc(sizeof(char) * length);
|
|
|
+ memset(file, 0, length);
|
|
|
+ snprintf(file, length, "%sstat", proc->path);
|
|
|
+ //Ouverture fichier
|
|
|
+ f = fopen(file, "r");;
|
|
|
+ if(!f){
|
|
|
+ free(file);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ free(file);
|
|
|
+ //Lecture des informations
|
|
|
+ tmp = fscanf(f, "%lld ", &trash);
|
|
|
+ tmp = fscanf(f, "%s ", buffer);
|
|
|
+ tmp = fscanf(f, "%c ", &proc->state);
|
|
|
+ for (int i = 0; i < 3; i++){
|
|
|
+ tmp = fscanf(f, "%lld ", &trash);
|
|
|
+ }
|
|
|
+ tmp = fscanf(f, "%lld ", &proc->tty);
|
|
|
+ for (int i = 0; i < 6; i++){
|
|
|
+ tmp = fscanf(f, "%lld ", &trash);
|
|
|
+ }
|
|
|
+ tmp = fscanf(f, "%lld ", &proc->cpu);
|
|
|
+ for (int i = 0; i < 7; i++){
|
|
|
+ tmp = fscanf(f, "%lld ", &trash);
|
|
|
+ }
|
|
|
+ tmp = fscanf(f, "%llu ", &proc->start);
|
|
|
+ tmp = fscanf(f, "%lld ", &proc->vsz);
|
|
|
+ tmp = fscanf(f, "%lld ", &proc->rss);
|
|
|
+ tmp++; //Pour ne pas avoir de warning
|
|
|
+ fclose(f);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Lecture du fichier comm d'un processus
|
|
|
+ * @param processus* Le processus
|
|
|
+ * @return Reussite
|
|
|
+ */
|
|
|
+boolean read_comm(processus* proc){
|
|
|
+ char* file;
|
|
|
+ int length, fd;
|
|
|
+ int size = 0;
|
|
|
+ char buf = ' ';
|
|
|
+ char* buffer;
|
|
|
+ boolean first = true;
|
|
|
+ //Recup nom du fichier
|
|
|
+ length = strlen(proc->path) + 5;
|
|
|
+ file = malloc(sizeof(char) * length);
|
|
|
+ memset(file, 0, length);
|
|
|
+ snprintf(file, length, "%scomm", proc->path);
|
|
|
+ //Ouverture fichier
|
|
|
+ fd = open(file, O_RDONLY);
|
|
|
+ if(fd == ERR){
|
|
|
+ free(file);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ free(file);
|
|
|
+ //Compte la taille du mot
|
|
|
+ while(buf != '\n' && buf != '\0' && buf != EOF){
|
|
|
+ if(read(fd, &buf, sizeof(char)) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //Au 1er tour
|
|
|
+ if(first){
|
|
|
+ //Si fichier vide
|
|
|
+ if(buf == ' '){
|
|
|
+ if(close(fd) == ERR){
|
|
|
+ //Rien de particulier
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+ size++;
|
|
|
+ }
|
|
|
+ size--;
|
|
|
+ //Revient au debut
|
|
|
+ if(lseek(fd, 0L, SEEK_SET) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //Lecture commande
|
|
|
+ buffer = malloc(sizeof(char) * (size + 3));
|
|
|
+ memset(buffer, 0, size + 3);
|
|
|
+ buffer[0] = '[';
|
|
|
+ if(read(fd, buffer + 1, size) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ buffer[strlen(buffer)] = ']';
|
|
|
+ proc->cmd = buffer;
|
|
|
+ //Fermeture
|
|
|
+ if(close(fd) == ERR){
|
|
|
+ //Rien de particulier
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Lecture du fichier cmdline d'un processus
|
|
|
+ * @param processus* Le processus
|
|
|
+ * @return Reussite
|
|
|
+ */
|
|
|
+boolean read_cmd(processus* proc){
|
|
|
+ char* file;
|
|
|
+ int length, fd;
|
|
|
+ int size = 0;
|
|
|
+ char buf = ' ';
|
|
|
+ char* buffer;
|
|
|
+ boolean first = true;
|
|
|
+ //Recup nom du fichier
|
|
|
+ length = strlen(proc->path) + 8;
|
|
|
+ file = malloc(sizeof(char) * length);
|
|
|
+ memset(file, 0, length);
|
|
|
+ snprintf(file, length, "%scmdline", proc->path);
|
|
|
+ //Ouverture fichier
|
|
|
+ fd = open(file, O_RDONLY);
|
|
|
+ if(fd == ERR){
|
|
|
+ free(file);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ free(file);
|
|
|
+ //Compte la taille du mot
|
|
|
+ while(buf != '\n' && buf != '\0' && buf != EOF){
|
|
|
+ if(read(fd, &buf, sizeof(char)) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //Au 1er tour
|
|
|
+ if(first){
|
|
|
+ //Si fichier vide
|
|
|
+ if(buf == ' '){
|
|
|
+ if(close(fd) == ERR){
|
|
|
+ //Rien de particulier
|
|
|
+ }
|
|
|
+ return read_comm(proc);
|
|
|
+ }
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+ size++;
|
|
|
+ //On evite les boucles infini
|
|
|
+ if(size > MAX_CMD_SIZE){
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ size--;
|
|
|
+ //Revient au debut
|
|
|
+ if(lseek(fd, 0L, SEEK_SET) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //Lecture commande
|
|
|
+ buffer = malloc(sizeof(char) * (size + 1));
|
|
|
+ memset(buffer, 0, size + 1);
|
|
|
+ if(read(fd, buffer, size) == ERR){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ proc->cmd = buffer;
|
|
|
+ //Fermeture
|
|
|
+ if(close(fd) == ERR){
|
|
|
+ //Rien de particulier
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Affiche les infos d'un processus
|
|
|
+ * @param processus* Le processus
|
|
|
+ */
|
|
|
+void printps(processus* proc){
|
|
|
+ struct passwd* user;
|
|
|
+ boolean color = false;
|
|
|
+ llint vsize;
|
|
|
+ char start[BUFFER_SIZE];
|
|
|
+ //Recup le nom de l'utilisateur
|
|
|
+ user = getpwuid(proc->uid);
|
|
|
+ if(user == NULL){
|
|
|
+ addperror("Impossible de récupérer le nom de l'utilisateur");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //VSZ
|
|
|
+ vsize = proc->vsz / 1024;
|
|
|
+ //Debut
|
|
|
+ int uptime = get_uptime();
|
|
|
+ int running = uptime - proc->start;
|
|
|
+ time_t runningTime = time(NULL) - (running / sysconf(_SC_CLK_TCK));
|
|
|
+ strftime(start, sizeof(start), "%H:%M", localtime(&runningTime));
|
|
|
+ //Couleur
|
|
|
+ if(proc->state == 's'){
|
|
|
+ printf(RED);
|
|
|
+ }
|
|
|
+ if(proc->state == 'r'){
|
|
|
+ printf(MAGENTA);
|
|
|
+ }
|
|
|
+ switch(proc->state){
|
|
|
+ case 'S':
|
|
|
+ printf(RED);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ case 'R':
|
|
|
+ printf(YELLOW);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ case 'N':
|
|
|
+ printf(BLUE);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ case 'Z':
|
|
|
+ printf(GREEN);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ case 'T':
|
|
|
+ printf(MAGENTA);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ case 'D':
|
|
|
+ printf(CYAN);
|
|
|
+ color = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ //Affiche (Manque %CPU, %MEM, TIME)
|
|
|
+ printf("%s %d %lld %lld %lld %c %s %s\n", user->pw_name, proc->pid, vsize, proc->rss, proc->tty, proc->state, start, proc->cmd);
|
|
|
+ if(color){
|
|
|
+ printf(RESET);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int main(){
|
|
|
+ int total;
|
|
|
+ processus** proc = list_process(&total);
|
|
|
+ //Tri des processus par rapport à leur pid
|
|
|
+ sort_pid(proc, total);
|
|
|
+ if(total > 0){
|
|
|
+ printf("USER PID VSZ RSS TTY STAT START COMMAND \n");
|
|
|
+ }
|
|
|
+ for(int i = 0; i < total; i++){
|
|
|
+ //Recup info manquante
|
|
|
+ if(!read_status(proc[i])){
|
|
|
+ free(proc[i]->path);
|
|
|
+ free(proc[i]);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!read_cmd(proc[i])){
|
|
|
+ free(proc[i]->path);
|
|
|
+ free(proc[i]);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //Affiche
|
|
|
+ printps(proc[i]);
|
|
|
+ //Supprime
|
|
|
+ free(proc[i]->path);
|
|
|
+ free(proc[i]->cmd);
|
|
|
+ free(proc[i]);
|
|
|
+ }
|
|
|
+ free(proc);
|
|
|
+ return EXIT_SUCCESS;
|
|
|
+}
|