Skip to content

Commit 7d8c046

Browse files
author
Charles PIGNEROL
committed
Pré-version 6.14.0. Classe FileLock, méthode ApplicationStats::logUsage (version, os).
1 parent db075b3 commit 7d8c046

File tree

11 files changed

+534
-92
lines changed

11 files changed

+534
-92
lines changed

src/TkUtil/ApplicationStats.cpp

Lines changed: 287 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
#include <TkUtil/Date.h>
1010
#include <TkUtil/File.h>
11+
#include <TkUtil/FileLock.h>
1112
#include <TkUtil/Threads.h>
1213
#include <TkUtil/UserData.h>
1314

1415
#include <map>
16+
#include <sstream>
1517

1618
#include <assert.h>
1719
#include <errno.h>
@@ -20,12 +22,15 @@
2022
#include <string.h> // strerror
2123
#include <sys/stat.h> // fchmod
2224
#include <sys/types.h>
25+
#include <sys/param.h> // MAXPATHLEN
2326
#include <unistd.h> // fork, setsid
2427

2528
USING_STD
2629

2730
BEGIN_NAMESPACE_UTIL
2831

32+
static const Charset charset ("àéèùô");
33+
2934

3035
ApplicationStats::ApplicationStats ( )
3136
{
@@ -195,7 +200,7 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
195200
bool found = false;
196201
int flag = 0;
197202
errno = 0;
198-
while (2 == (flag = fscanf (file, "%s\t%u", name, &count)))
203+
while (2 == (flag = fscanf (file, "%s\t%lu", name, &count)))
199204
{
200205
line++;
201206
if (name == user)
@@ -205,7 +210,7 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
205210
} // if (name == user)
206211
logs.insert (pair<string, size_t> (name, count));
207212
count = 0;
208-
} // while (2 == fscanf (file, "%s\t%u", name, &count))
213+
} // while (2 == fscanf (file, "%s\t%lu", name, &count))
209214
if (0 != errno)
210215
{
211216
{
@@ -241,15 +246,15 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
241246

242247
for (map<string, size_t>::const_iterator itl = logs.begin ( ); logs.end ( ) != itl; itl++)
243248
{
244-
if (fprintf (file, "%s\t%u\n", (*itl).first.c_str ( ), (*itl).second) < 0)
249+
if (fprintf (file, "%s\t%lu\n", (*itl).first.c_str ( ), (*itl).second) < 0)
245250
{
246251
{
247252
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
248253
ConsoleOutput::cerr ( ) << "Erreur lors de la réécriture du fichier de logs " << fileName << "."<< co_endl;
249254
}
250255
fclose (file);
251256
exit (0);
252-
}
257+
} // if (fprintf (file, "%s\t%lu\n", (*itl).first.c_str ( ), (*itl).second) < 0)
253258
} // for (map<string, size_t>::const_iterator itl = logs.begin ( ); logs.end ( ) != itl; itl++)
254259
errno = 0;
255260
if (0 != fflush (file))
@@ -281,6 +286,177 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)
281286
} // ApplicationStats::logUsage
282287

283288

289+
/**
290+
* Structure portant les informations à enregistrer dans un fichier, à savoir la version de l'application, le système d'exploitation, et le chemin
291+
* d'accès complet à l'application.
292+
*/
293+
struct OriginInfos
294+
{
295+
OriginInfos (const string& v, const string& o, const string& p)
296+
: version (v), os (o), path (p), key ( )
297+
{
298+
ostringstream s;
299+
s << version << os << path;
300+
key = s.str ( );
301+
}
302+
OriginInfos (const OriginInfos& oi)
303+
: version (oi.version), os (oi.os), path (oi.path), key (oi.key)
304+
{ }
305+
OriginInfos& operator = (const OriginInfos& oi)
306+
{
307+
if (&oi != this)
308+
{
309+
version = oi.version;
310+
os = oi.os;
311+
path = oi.path;
312+
key = oi.key;
313+
}
314+
315+
return *this;
316+
}
317+
string version, os, path;
318+
string key;
319+
}; // struct OriginInfos
320+
321+
322+
static bool operator == (const OriginInfos& left, const OriginInfos& right)
323+
{
324+
return (left.version == right.version) && (left.os == right.os) && (left.path == right.path) ? true : false;
325+
} // operator == (const OriginInfos& left, const OriginInfos& right)
326+
327+
328+
static bool operator != (const OriginInfos& left, const OriginInfos& right)
329+
{
330+
return !(left == right);
331+
} // operator != (const OriginInfos& left, const OriginInfos& right)
332+
333+
334+
static bool operator < (const OriginInfos& left, const OriginInfos& right)
335+
{
336+
return left.key < right.key;
337+
} // operator < (const OriginInfos& left, const OriginInfos& right)
338+
339+
340+
341+
void ApplicationStats::logUsage (const string& appName, const string& logDir, const string& version, const string& os, const string& path)
342+
{
343+
FILE* file = 0;
344+
string fileName;
345+
346+
try
347+
{
348+
if ((true == appName.empty ( )) || (true == logDir.empty ( )))
349+
{
350+
{
351+
352+
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : nom d'application ou répertoire des logs non renseigné (" << appName << "/" << logDir << ")." << co_endl;
353+
}
354+
355+
return; // Tolérance aux erreurs
356+
} // if ((true == appName.empty ( )) || (true == logDir.empty ( )))
357+
358+
// Le nom du fichier :
359+
const Date date;
360+
const string user (UserData (true).getName ( ));
361+
fileName = getFileName (appName + string ("_usages"), logDir, (unsigned long)date.getMonth ( ), date.getYear ( ));
362+
363+
file = initLogSession (fileName);
364+
if (0 == file)
365+
return; // Process père
366+
367+
{ // FileLock scope
368+
FileLock logLock (fileName, file);
369+
370+
// Lecture et actualisation des logs existants :
371+
map<OriginInfos, size_t> logs;
372+
char v [255], o [255], p [MAXPATHLEN + 1];
373+
size_t count = 0, line = 1;
374+
bool found = false;
375+
int flag = 0;
376+
377+
errno = 0;
378+
const OriginInfos addedOi (version, os, path);
379+
while (4 == (flag = fscanf (file, "%s\t%s\t%s\t%lu", v, o, p, &count)))
380+
{
381+
line++;
382+
const OriginInfos oi (v, o, p);
383+
if (addedOi == oi)
384+
{
385+
found = true;
386+
count++;
387+
} // if (addedOi == oi)
388+
logs.insert (pair<OriginInfos, size_t>(oi, count));
389+
count = 0;
390+
} // while (4 == (flag = fscanf (file, "%s\t%s\t%s\t%lu", v, o, p, &count)))
391+
392+
if (0 != errno)
393+
{
394+
UTF8String error (charset);
395+
error << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << strerror (errno);
396+
errno = 0;
397+
throw Exception (error);
398+
} // if (0 != errno)
399+
else if ((flag < 4) && (EOF != flag))
400+
{
401+
UTF8String error (charset);
402+
error << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : fichier probablement corrompu.";
403+
throw Exception (error);
404+
} // if ((flag < 4) && (EOF != flag))
405+
406+
if (false == found)
407+
logs.insert (pair<OriginInfos, size_t>(addedOi, 1));
408+
409+
// Réécriture des logs actualisés :
410+
errno = 0;
411+
if (0 != fseek (file, 0, SEEK_SET))
412+
{
413+
UTF8String error (charset);
414+
error << "Erreur lors de la réécriture du fichier de logs " << fileName << " : " << strerror (errno);
415+
errno = 0;
416+
throw Exception (error);
417+
} // if (0 != fseek (file, 0, SEEK_SET))
418+
419+
for (map<OriginInfos, size_t>::const_iterator itl = logs.begin ( ); logs.end ( ) != itl; itl++)
420+
{
421+
if (fprintf (file, "%s\t%s\t%s\t%lu\n", (*itl).first.version.c_str ( ), (*itl).first.os.c_str ( ),(*itl).first.path.c_str ( ), (*itl).second) < 0)
422+
{
423+
UTF8String error (charset);
424+
error << "Erreur lors de la réécriture du fichier de logs " << fileName << ".";
425+
throw Exception (error);
426+
} // if (fprintf (file, "%s\t%s\t%s\t%lu\n", (*itl).first.version.c_str ( ), (*itl).first.os.c_str ( ),(*itl).first.path.c_str ( ), (*itl).second) < 0)
427+
} // for (map<OriginInfos, size_t>::const_iterator itl = logs.begin ( ); logs.end ( ) != itl; itl++)
428+
429+
errno = 0;
430+
if (0 != fflush (file))
431+
{
432+
UTF8String error (charset);
433+
error << "Erreur lors de la réécriture du fichier de logs " << fileName << " : " << strerror (errno);
434+
errno = 0;
435+
throw Exception (error);
436+
} // if (0 != fflush (file))
437+
} // FileLock scope
438+
}
439+
catch (const Exception& exc)
440+
{
441+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
442+
ConsoleOutput::cerr ( ) << exc.getFullMessage ( ) << co_endl;
443+
}
444+
catch (...)
445+
{
446+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
447+
ConsoleOutput::cerr ( ) << "ApplicationStats::logUsage : erreur non renseignée lors de l'écriture d'informations dans le fichier \"" << fileName << "\"." << co_endl;
448+
}
449+
450+
if (0 != file)
451+
{
452+
fclose (file);
453+
file = 0;
454+
} // if (0 != file)
455+
456+
exit (0);
457+
} // ApplicationStats::logUsage
458+
459+
284460
void ApplicationStats::logStats (std::ostream& output, const std::string& appName, const string& from, const string& to, const std::string& logDir)
285461
{
286462
map<string, size_t> logs;
@@ -335,6 +511,111 @@ void ApplicationStats::logStats (std::ostream& output, const std::string& appNam
335511
} // ApplicationStats::logStats
336512

337513

514+
FILE* ApplicationStats::initLogSession (const string fullFileName)
515+
{
516+
// 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.
517+
errno = 0;
518+
const pid_t pid = fork ( );
519+
if ((pid_t)-1 == pid)
520+
{
521+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
522+
ConsoleOutput::cerr ( ) << "ApplicationStats::initLogSession : échec de fork : " << strerror (errno) << co_endl;
523+
return 0;
524+
} // if ((pid_t)-1 == pid)
525+
if (0 != pid)
526+
{
527+
Process::killAtEnd (pid);
528+
return 0; // Parent
529+
}
530+
531+
// On détache complètement le fils du parent => peut importe qui fini en premier, l'autre ira jusqu'au bout :
532+
const pid_t sid = setsid ( );
533+
if ((pid_t)-1 == sid)
534+
{
535+
{
536+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
537+
ConsoleOutput::cerr ( ) << "ApplicationStats::initLogSession : échec de setsid : " << strerror (errno) << co_endl;
538+
}
539+
exit (0);
540+
} // if ((pid_t)-1 == sid)
541+
542+
// On ouvre le fichier en lecture/écriture :
543+
FILE* file = fopen (fullFileName.c_str ( ), "r+"); // Ne créé pas le fichier => on le créé ci-dessous si nécessaire :
544+
const bool created = NULL == file ? true : false;
545+
file = NULL == file ? fopen (fullFileName.c_str ( ), "a+") : file;
546+
if (NULL == file)
547+
{
548+
try
549+
{ // On peut avoir des exceptions de levées : chemin non traversable, ...
550+
File logFile (fullFileName);
551+
File dir = logFile.getPath ( );
552+
if ((false == dir.exists ( )) || (false == dir.isDirectory ( )) || (false == dir.isExecutable ( )) || (false == dir.isWritable ( )))
553+
{
554+
{
555+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
556+
ConsoleOutput::cerr ( ) << "Erreur, " << dir.getFullFileName ( ) << " n'est pas un répertoire existant avec les droits en écriture pour vous." << co_endl;
557+
}
558+
exit (0);
559+
} // if ((false == dir.exists ( )) || (false == dir.isDirectory ( )) || ...
560+
if (false == logFile.isWritable ( ))
561+
{
562+
{
563+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
564+
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fullFileName << " : absence de droits en écriture." << co_endl;
565+
}
566+
exit (0);
567+
} // if (false == logFile.isWritable ( ))
568+
}
569+
catch (const Exception& exc)
570+
{
571+
{
572+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
573+
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fullFileName << " : " << exc.getFullMessage ( ) << co_endl;
574+
}
575+
exit (0);
576+
}
577+
catch (...)
578+
{
579+
}
580+
581+
{
582+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
583+
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fullFileName << " : erreur non documentée." << co_endl;
584+
}
585+
586+
exit (0);
587+
} // if (NULL == file)
588+
589+
// Obtenir le descripteur de fichier :
590+
int fd = fileno (file);
591+
if (-1 == fd)
592+
{
593+
{
594+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
595+
ConsoleOutput::cerr ( ) << "Erreur lors de l'ouverture du fichier de logs " << fullFileName << co_endl;
596+
}
597+
exit (0);
598+
} // if (-1 == fd)
599+
600+
// Conférer aufichier les droits en écriture pour tous le monde si il vient d'être créé :
601+
if (true == created)
602+
{
603+
if (0 != fchmod (fd, S_IRWXU | S_IRWXG | S_IRWXO))
604+
{
605+
{
606+
TermAutoStyle as (cerr, AnsiEscapeCodes::blueFg);
607+
ConsoleOutput::cerr ( ) << "Erreur lors du confèrement à autrui des droits en écriture sur le fichier de logs " << fullFileName << " : " << strerror (errno) << co_endl;
608+
}
609+
fclose (file);
610+
exit (0);
611+
612+
} // if (0 != fchmod (fd)
613+
} // if (true == created)
614+
615+
return file;
616+
} // ApplicationStats::initLogSession
617+
618+
338619
int ApplicationStats::readLogs (const string& fileName, map<string, size_t>& logs)
339620
{
340621
File logFile (fileName);
@@ -360,7 +641,7 @@ int ApplicationStats::readLogs (FILE& file, const string& fileName, map<string,
360641
size_t line = 1, count = 0;
361642
int flag = 0;
362643
char name [256];
363-
while (2 == (flag = fscanf (&file, "%s\t%u", name, &count)))
644+
while (2 == (flag = fscanf (&file, "%s\t%lu", name, &count)))
364645
{
365646
line++;
366647
map<string, size_t>::iterator itl = logs.find (name);
@@ -369,7 +650,7 @@ int ApplicationStats::readLogs (FILE& file, const string& fileName, map<string,
369650
else
370651
(*itl).second += count;
371652
count = 0;
372-
} // while (2 == fscanf (file, "%s\t%u", name, &count))
653+
} // while (2 == fscanf (file, "%s\t%lu", name, &count))
373654
if ((flag < 2) && (EOF != flag))
374655
{
375656
ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : fichier probablement corrompu." << co_endl;

src/TkUtil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ foreach (lib ${ALL_TARGETS})
2121
target_compile_options (${lib} PUBLIC ${MANDATORY_CXX_OPTIONS})
2222
target_compile_definitions (${lib} PRIVATE TOSTR_USES_CPP_IMPLEMENTATION)
2323
target_compile_definitions (${lib} PRIVATE UTIL_VERSION="${TK_UTIL_VERSION}")
24+
target_compile_definitions (${lib} PRIVATE OS_DISTRIBUTION="${PLATFORM}") # défini dans workarounds.cmake
2425
set_property (TARGET ${lib} PROPERTY VERSION ${TK_UTIL_VERSION})
2526
set_property (TARGET ${lib} PROPERTY SOVERSION ${TK_UTIL_MAJOR_VERSION})
2627
# Etre capable une fois installée de retrouver les libs utilisées (iconv ?), ... :

0 commit comments

Comments
 (0)