Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmake/version.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#

set (TK_UTIL_MAJOR_VERSION "6")
set (TK_UTIL_MINOR_VERSION "12")
set (TK_UTIL_RELEASE_VERSION "1")
set (TK_UTIL_MINOR_VERSION "13")
set (TK_UTIL_RELEASE_VERSION "0")
set (TK_UTIL_VERSION ${TK_UTIL_MAJOR_VERSION}.${TK_UTIL_MINOR_VERSION}.${TK_UTIL_RELEASE_VERSION})

set (TK_UTIL_SCRIPTING_MAJOR_VERSION ${TK_UTIL_MAJOR_VERSION})
Expand Down
86 changes: 67 additions & 19 deletions src/TkUtil/ApplicationStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "TkUtil/Exception.h"
#include "TkUtil/File.h"
#include "TkUtil/NumericConversions.h"
#include "TkUtil/Process.h"
#include "TkUtil/UTF8String.h"

#include <TkUtil/Date.h>
Expand Down Expand Up @@ -62,30 +63,38 @@ string ApplicationStats::getFileName (const string& appName, const string& logDi

void ApplicationStats::logUsage (const string& appName, const string& logDir)
{
// En vue de ne pas altérer le comportement de l'application tout est effectuée dans un processus fils.
// Par ailleurs on quitte le processus fils par return et non exit pour passer dans le destructeur des variables automatiques créées (TermAutoStyle, ...).
// En vue de ne pas altérer le comportement de l'application tout est effectuée dans un processus fils => exit (0) en toutes circonstances.
errno = 0;
const pid_t pid = fork ( );
if ((pid_t)-1 == pid)
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : échec de fork : " << strerror (errno) << co_endl;
return;
} // if ((pid_t)-1 == pid)
if (0 != pid)
{
Process::killAtEnd (pid);
return; // Parent
}

// On détache complètement le fils du parent => peut importe qui fini en premier, l'autre ira jusqu'au bout :
const pid_t sid = setsid ( );
if ((pid_t)-1 == sid)
{
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : échec de setsid : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : échec de setsid : " << strerror (errno) << co_endl;
}
exit (0);
} // if ((pid_t)-1 == sid)

TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
if ((true == appName.empty ( )) || (true == logDir.empty ( )))
{
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : nom d'application ou répertoire des logs non renseigné (" << appName << "/" << logDir << ")." << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : nom d'application ou répertoire des logs non renseigné (" << appName << "/" << logDir << ")." << co_endl;
}
exit (0);
} // if ((true == appName.empty ( )) || (true == logDir.empty ( )))

Expand All @@ -105,26 +114,38 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
File dir (logDir);
if ((false == dir.exists ( )) || (false == dir.isDirectory ( )) || (false == dir.isExecutable ( )) || (false == dir.isWritable ( )))
{
ConsoleOutput::cerr ( ) << "Erreur, " << logDir << " n'est pas un répertoire existant avec les droits en écriture pour vous." << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur, " << logDir << " n'est pas un répertoire existant avec les droits en écriture pour vous." << co_endl;
}
exit (0);
} // if ((false == dir.exists ( )) || (false == dir.isDirectory ( )) || ...
File logFile (fileName.utf8 ( ));
if (false == logFile.isWritable ( ))
{
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : absence de droits en écriture." << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : absence de droits en écriture." << co_endl;
}
exit (0);
} // if (false == logFile.isWritable ( ))
}
catch (const Exception& exc)
{
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : " << exc.getFullMessage ( ) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : " << exc.getFullMessage ( ) << co_endl;
}
exit (0);
}
catch (...)
{
}

ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : erreur non documentée." << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << " : erreur non documentée." << co_endl;
}

exit (0);
} // if (NULL == file)
Expand All @@ -133,15 +154,21 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
int fd = fileno (file);
if (-1 == fd)
{
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fileName << co_endl;
}
exit (0);
} // if (-1 == fd)

// Appliquer un verrou exclusif sur le fichier de logs :
errno = 0;
if (0 != flock (fd, LOCK_EX))
{
ConsoleOutput::cerr ( ) << "Erreur lors du verrouillage du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors du verrouillage du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
}
fclose (file);
exit (0);
} // if (0 != flock (fd, LOCK_EX))
Expand All @@ -151,7 +178,10 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
{
if (0 != fchmod (fd, S_IRWXU | S_IRWXG | S_IRWXO))
{
ConsoleOutput::cerr ( ) << "Erreur lors du confèrement à autrui des droits en écriture sur le fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors du confèrement à autrui des droits en écriture sur le fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
}
fclose (file);
exit (0);

Expand All @@ -178,13 +208,19 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
} // while (2 == fscanf (file, "%s\t%u", name, &count))
if (0 != errno)
{
ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : " << strerror (errno) << co_endl;
}
fclose (file);
exit (0);
} // if (0 != errno)
else if ((flag < 2) && (EOF != flag))
{
ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : fichier probablement corrompu." << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : fichier probablement corrompu." << co_endl;
}
fclose (file);
exit (0);
} // if (flag < 2)
Expand All @@ -195,7 +231,10 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
errno = 0;
if (0 != fseek (file, 0, SEEK_SET))
{
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
}
fclose (file);
exit (0);
} // if (0 != fseek (file, 0, SEEK_SET))
Expand All @@ -204,24 +243,33 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
{
if (fprintf (file, "%s\t%u\n", (*itl).first.c_str ( ), (*itl).second) < 0)
{
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << "."<< co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << "."<< co_endl;
}
fclose (file);
exit (0);
}
} // for (map<string, size_t>::const_iterator itl = logs.begin ( ); logs.end ( ) != itl; itl++)
errno = 0;
if (0 != fflush (file))
{
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
fclose (file);
exit (0);
}
fclose (file);
exit (0);
} // if (0 != fflush (file))

// Libération du verrou :
errno = 0;
if (0 != flock (fd, LOCK_UN))
{
ConsoleOutput::cerr ( ) << "Erreur lors du déverrouillage du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Erreur lors du déverrouillage du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;
}
fclose (file);
} // if (0 != flock (fd, LOCK_UN))

Expand Down
24 changes: 24 additions & 0 deletions src/TkUtil/Process.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "TkUtil/AnsiEscapeCodes.h"
#include "TkUtil/Process.h"
#include "TkUtil/File.h"
#include "TkUtil/UTF8String.h"
Expand Down Expand Up @@ -80,6 +81,7 @@ const vector<string>& Process::ProcessOptions::getOptions ( ) const
// ===========================================================================

vector<Process*> Process::_tasks;
set<pid_t> Process::_toKill;
int Process::_argc = 0;
char** Process::_argv = 0;
char** Process::_envp = 0;
Expand Down Expand Up @@ -444,6 +446,28 @@ void Process::initialize (char* envp [])
} // Process::initialize


void Process::finalize ( )
{
for (set<pid_t>::iterator itk = _toKill.begin ( ); _toKill.end ( ) != itk; itk++)
{
int res = ::kill (*itk, SIGKILL);
if (0 != res)
{
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
ConsoleOutput::cerr ( ) << "Process::finalize. Erreur lors de l'appel à kill pour le processus de PID " << (unsigned long)*itk << " : " << strerror (errno) << co_endl;
} // if (0 != res)
} // for (set<pid_t>::iterator itk = _toKill.begin ( ); _toKill.end ( ) != itk; itk++)

_toKill.clear ( );
} // Process::finalize


void Process::killAtEnd (pid_t tokill)
{
_toKill.insert (tokill);
} // Process::killAtEnd


string Process::getChildLine ( )
{
if ((true == isCompleted ( )) && (0 == _childBuffer.length ( )))
Expand Down
25 changes: 24 additions & 1 deletion src/TkUtil/public/TkUtil/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <siginfo.h>
#endif // !defined(__GNUC__) && !defined(__ia64__)

#include <set>
#include <vector>

#define PROCESS_ERROR_MESSAGE_SIZE (1025)
Expand Down Expand Up @@ -286,7 +287,8 @@ class Process
* @param environnement d'execution (3eme argument du main de l'application, où main est défini comme suit :
* int main (int argc, char* argv[], char* envp [])
* @warning il est essentiel d'appeler cette fonction avant le lancement de tout process fils. En son absence
* il est possible que l'exécution du process fils échoue.
* il est possible que l'exécution du process fils échoue.
* @see finalize
*/
static void initialize (int argc, char* argv [], char* envp []);

Expand All @@ -300,6 +302,21 @@ class Process
*/
static void initialize (char* envp []);

/**
* Détruit tous les processus dont la destruction a été confiée à cette classe. Ce peut être par exemple des processus
* fils lancés via fork et non attendus.
* @see killAtEnd
* @since 6.13.0
*/
static void finalize ( );

/**
* Délègue à cette classe le fait de tuer le processus de pid transmis en argument lorsque Process::finalize sera appelé.
* @see finalize
* @since 6.13.0
*/
static void killAtEnd (pid_t tokill);

/**
* @return Le répertoire courrant de l'application, ou, en cas d'erreur, une chaine vide.
*/
Expand Down Expand Up @@ -455,6 +472,12 @@ class Process
/** La table comprenant les ids des tâches et les pointeurs sur les instances associées de cette classe.
*/
static IN_STD vector<Process*> _tasks;

/**
* Les processus à tuer à lappel de finalize.
* @see killAtEnd
*/
static IN_STD set<pid_t> _toKill;

/** Le nombre d'arguments de lancement de cette application. */
static int _argc;
Expand Down
6 changes: 6 additions & 0 deletions versions.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version 6.13.0 : 14/03/25
=================

Possibilité de déléguer à la classe Process la destruction de processus en fin d'exécution via Process::killAtEnd () et Process::finalize ().


Version 6.12.1 : 12/03/25
=================

Expand Down