Commit 908a31be authored by Rob Swindell's avatar Rob Swindell 💬
Browse files

Overhaul statistics files

*/dsts.dab (daily statistics and running totals) -> */dsts.ini
*/csts.dab (cumulative statistics / log) -> */csts.tab

* dsts.ini now has both daily and total stats for all fields (not just timeon and logons).
* dsts.ini is now an easily modifiable text file - no longer need dstsedit (here-by deprecated and soon to be deleted)
* dsts.ini and csts.tab support 64-bit upload/download byte stats and are very extensible for future fields to be added or extended > 32-bit (this was the main inspiration for this overhaul, but it was overdue and already designed for v4, pretty much)
* csts.tab is a tab-delimited fixed length record format suitable for easy import to a spreadsheet program or parsing with scripts. Each day is a 128-character LF-delimited record with tab-delimited fields of plain ASCII text.
* All fields except timeon in dsts.ini files are updated immediately and by more non-terminal servers (e.g. post statistics from web UI scripts).
* New user stats are tracked more than just for "today".

The upgrade of these files is automatic and built-into SBBS.

Still to do: overhaul the slog utility to support the new csts.tab file format.
parent efe3b357
......@@ -26,6 +26,7 @@
#include "userdat.h"
#include "filedat.h"
#include "load_cfg.h"
#include "getstats.h"
#include "smblib.h"
#include "git_branch.h"
#include "git_hash.h"
......@@ -77,29 +78,11 @@ int lprintf(int level, const char *fmat, ...)
}
/****************************************************************************/
/* Updates dstst.dab file */
/* Updates dsts.ini file */
/****************************************************************************/
void updatestats(ulong size)
void updatestats(off_t size)
{
char str[MAX_PATH+1];
int file;
uint32_t l;
SAFEPRINTF(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR|O_BINARY))==-1) {
printf("ERR_OPEN %s\n",str);
return;
}
lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&l,4); /* Uploads today */
l++;
lseek(file,-4L,SEEK_CUR);
write(file,&l,4);
read(file,&l,4); /* Upload bytes today */
l+=size;
lseek(file,-4L,SEEK_CUR);
write(file,&l,4);
close(file);
inc_upload_stats(&scfg, 1, size);
}
bool reupload(smb_t* smb, file_t* f)
......@@ -184,9 +167,9 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
memset(ext, 0, sizeof(ext));
memset(&f, 0, sizeof(f));
char fdesc[LEN_FDESC + 1] = {0};
uint32_t cdt = (uint32_t)flength(filepath);
off_t cdt = flength(filepath);
time_t file_timestamp = fdate(filepath);
printf("%10"PRIu32" %s\n"
printf("%10"PRIuOFF" %s\n"
,cdt, unixtodstr(&scfg,(time32_t)file_timestamp,str));
exist = smb_findfile(&smb, fname, &f) == SMB_SUCCESS;
if(exist) {
......@@ -418,7 +401,7 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
, result, smb.last_error, smb.file);
if(mode&UL_STATS)
updatestats((ulong)l);
updatestats(l);
files++;
} while(!feof(stream) && !ferror(stream));
fclose(stream);
......
......@@ -176,6 +176,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
......
......@@ -162,6 +162,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -162,6 +162,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -170,6 +170,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -807,7 +807,7 @@ static void send_thread(void* arg)
}
}
if(!xfer.tmpfile && !xfer.delfile && !(scfg.dir[f.dir]->misc&DIR_NOSTAT))
inc_sys_download_stats(&scfg, 1, (ulong)total);
inc_download_stats(&scfg, 1, (ulong)total);
}
if(xfer.credits) {
......@@ -1078,7 +1078,7 @@ static void receive_thread(void* arg)
,cdt*(uint64_t)(scfg.dir[f.dir]->up_pct/100.0));
}
if(!(scfg.dir[f.dir]->misc&DIR_NOSTAT))
inc_sys_upload_stats(&scfg, 1, (ulong)total);
inc_upload_stats(&scfg, 1, (ulong)total);
}
/* Send ACK */
sockprintf(xfer.ctrl_sock,sess,"226 Upload complete (%lu cps).",cps);
......
/* Synchronet C-exported statistics retrieval functions */
/* Synchronet C-exported statistics retrieval and update functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
......@@ -22,25 +22,275 @@
#include "getstats.h"
#include "nopen.h"
#include "smblib.h"
#include "ini_file.h"
#include "xpendian.h"
#include "xpdatetime.h"
// dsts.ini (Daily Statistics) keys:
#define strStatsDate "Date"
#define strStatsTotal "Total"
#define strStatsToday "Today"
#define strStatsLogons "Logons"
#define strStatsTimeon "Timeon"
#define strStatsUploads "Uploads"
#define strStatsUploadBytes "UploadB"
#define strStatsDownloads "Dnloads"
#define strStatsDownloadBytes "DnloadB"
#define strStatsPosts "Posts"
#define strStatsEmail "Email"
#define strStatsFeedback "Feedback"
#define strStatsNewUsers "NewUsers"
/****************************************************************************/
/* Daily statistics (reset daily) */
/****************************************************************************/
char* dstats_fname(scfg_t* cfg, uint node, char* path, size_t size)
{
safe_snprintf(path, size, "%sdsts.ini", node > 0 && node <= cfg->sys_nodes ? cfg->node_path[node-1] : cfg->ctrl_dir);
return path;
}
/****************************************************************************/
/* Cumulative statistics (log) */
/****************************************************************************/
char* cstats_fname(scfg_t* cfg, uint node, char* path, size_t size)
{
safe_snprintf(path, size, "%scsts.tab", node > 0 && node <= cfg->sys_nodes ? cfg->node_path[node-1] : cfg->ctrl_dir);
return path;
}
/****************************************************************************/
/****************************************************************************/
FILE* fopen_dstats(scfg_t* cfg, uint node, BOOL for_write)
{
char path[MAX_PATH+1];
dstats_fname(cfg, node, path, sizeof(path));
return iniOpenFile(path, for_write);
}
/****************************************************************************/
/****************************************************************************/
FILE* fopen_cstats(scfg_t* cfg, uint node, BOOL for_write)
{
char path[MAX_PATH+1];
cstats_fname(cfg, node, path, sizeof(path));
return fnopen(NULL, path, for_write ? O_CREAT|O_WRONLY|O_APPEND : O_RDONLY);
}
/****************************************************************************/
/****************************************************************************/
BOOL fclose_dstats(FILE* fp)
{
return iniCloseFile(fp);
}
/****************************************************************************/
/* Reads data from dsts.dab into stats structure */
/* If node is zero, reads from ctrl\dsts.dab, otherwise from each node */
/****************************************************************************/
BOOL getstats(scfg_t* cfg, char node, stats_t* stats)
BOOL fclose_cstats(FILE* fp)
{
char str[MAX_PATH+1];
int file;
return fclose(fp) == 0;
}
static void gettotals(str_list_t ini, const char* section, totals_t* stats)
{
stats->logons = iniGetLongInt(ini, section, strStatsLogons, 0);
stats->timeon = iniGetLongInt(ini, section, strStatsTimeon, 0);
stats->uls = iniGetLongInt(ini, section, strStatsUploads, 0);
stats->ulb = iniGetBytes(ini, section, strStatsUploadBytes, /* unit: */1, 0);
stats->dls = iniGetLongInt(ini, section, strStatsDownloads, 0);
stats->dlb = iniGetBytes(ini, section, strStatsDownloadBytes, /* unit: */1, 0);
stats->posts = iniGetLongInt(ini, section, strStatsPosts, 0);
stats->email = iniGetLongInt(ini, section, strStatsEmail, 0);
stats->fbacks = iniGetLongInt(ini, section, strStatsFeedback, 0);
stats->nusers = iniGetLongInt(ini, section, strStatsNewUsers, 0);
}
/****************************************************************************/
/* Reads data from dsts.ini into stats structure */
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL fread_dstats(FILE* fp, stats_t* stats)
{
str_list_t ini;
if(fp == NULL)
return FALSE;
memset(stats, 0, sizeof(*stats));
ini = iniReadFile(fp);
stats->date = iniGetDateTime(ini, NULL, strStatsDate, 0);
gettotals(ini, strStatsToday, &stats->today);
gettotals(ini, strStatsTotal, &stats->total);
iniFreeStringList(ini);
return TRUE;
}
/****************************************************************************/
/* Reads data from dsts.ini into stats structure */
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL getstats(scfg_t* cfg, uint node, stats_t* stats)
{
char path[MAX_PATH+1];
BOOL result;
memset(stats, 0, sizeof(*stats));
dstats_fname(cfg, node, path, sizeof(path));
FILE* fp = fnopen(NULL, path, O_RDONLY);
if(fp == NULL) {
int file;
if(fexist(path))
return FALSE;
// Upgrading from v3.19?
struct { /* System/Node Statistics */
uint32_t date, /* When last rolled-over */
logons, /* Total Logons on System */
ltoday, /* Total Logons Today */
timeon, /* Total Time on System */
ttoday, /* Total Time Today */
uls, /* Total Uploads Today */
ulb, /* Total Upload Bytes Today */
dls, /* Total Downloads Today */
dlb, /* Total Download Bytes Today */
ptoday, /* Total Posts Today */
etoday, /* Total Emails Today */
ftoday; /* Total Feedbacks Today */
uint16_t nusers; /* Total New Users Today */
} legacy_stats;
SAFEPRINTF(path,"%sdsts.dab",node ? cfg->node_path[node-1] : cfg->ctrl_dir);
if((file=nopen(path,O_RDONLY))==-1) {
return(FALSE);
}
read(file, &legacy_stats, sizeof(legacy_stats));
close(file);
sprintf(str,"%sdsts.dab",node ? cfg->node_path[node-1] : cfg->ctrl_dir);
if((file=nopen(str,O_RDONLY))==-1) {
return(FALSE);
stats->date = LE_INT(legacy_stats.date);
stats->logons = LE_INT(legacy_stats.logons);
stats->ltoday = LE_INT(legacy_stats.ltoday);
stats->timeon = LE_INT(legacy_stats.timeon);
stats->ttoday = LE_INT(legacy_stats.ttoday);
stats->uls = LE_INT(legacy_stats.uls);
stats->ulb = LE_INT(legacy_stats.ulb);
stats->dls = LE_INT(legacy_stats.dls);
stats->dlb = LE_INT(legacy_stats.dlb);
stats->ptoday = LE_INT(legacy_stats.ptoday);
stats->etoday = LE_INT(legacy_stats.etoday);
stats->ftoday = LE_INT(legacy_stats.ftoday);
stats->nusers = LE_INT(legacy_stats.nusers);
return TRUE;
}
lseek(file,4L,SEEK_SET); /* Skip update time/date */
/* TODO: Direct read of unpacked struct */
read(file,stats,sizeof(stats_t));
close(file);
return(TRUE);
result = fread_dstats(fp, stats);
fclose(fp);
return result;
}
static void settotals(str_list_t* ini, const char* section, const totals_t* stats)
{
iniSetLongInt(ini, section, strStatsLogons, stats->logons, /* style: */NULL);
iniSetLongInt(ini, section, strStatsTimeon, stats->timeon, /* style: */NULL);
iniSetLongInt(ini, section, strStatsUploads, stats->uls, /* style: */NULL);
iniSetBytes(ini, section, strStatsUploadBytes, /* unit: */1, stats->ulb, /* style: */NULL);
iniSetLongInt(ini, section, strStatsDownloads, stats->dls, /* style: */NULL);
iniSetBytes(ini, section, strStatsDownloadBytes, /* unit: */1, stats->dlb, /* style: */NULL);
iniSetLongInt(ini, section, strStatsPosts, stats->posts, /* style: */NULL);
iniSetLongInt(ini, section, strStatsEmail, stats->email, /* style: */NULL);
iniSetLongInt(ini, section, strStatsFeedback, stats->fbacks, /* style: */NULL);
iniSetLongInt(ini, section, strStatsNewUsers, stats->nusers, /* style: */NULL);
}
/****************************************************************************/
/****************************************************************************/
BOOL fwrite_dstats(FILE* fp, const stats_t* stats)
{
BOOL result;
str_list_t ini;
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetDateTime(&ini, NULL, strStatsDate, /* include_time: */FALSE, stats->date, /* style: */NULL);
settotals(&ini, strStatsToday, &stats->today);
settotals(&ini, strStatsTotal, &stats->total);
result = iniWriteFile(fp, ini);
iniFreeStringList(ini);
return result;
}
/****************************************************************************/
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL putstats(scfg_t* cfg, uint node, const stats_t* stats)
{
BOOL result;
FILE* fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
result = fwrite_dstats(fp, stats);
iniCloseFile(fp);
return result;
}
/****************************************************************************/
/****************************************************************************/
void rolloverstats(stats_t* stats)
{
stats->date = time(NULL);
memset(&stats->today, 0, sizeof(stats->today));
}
/****************************************************************************/
/****************************************************************************/
BOOL fwrite_cstats(FILE* fp, const stats_t* stats)
{
int len;
char pad[LEN_CSTATS_RECORD];
memset(pad, '\t', sizeof(pad) - 1);
TERMINATE(pad);
fseek(fp, 0, SEEK_END);
if(ftell(fp) == 0) {
len = fprintf(fp
,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
,strStatsDate
,strStatsLogons
,strStatsTimeon
,strStatsUploads
,strStatsUploadBytes
,strStatsDownloads
,strStatsDownloadBytes
,strStatsPosts
,strStatsEmail
,strStatsFeedback
,strStatsNewUsers
);
if(len >= sizeof(pad))
return FALSE;
if(fprintf(fp, "%.*s\n", sizeof(pad) - (len + 1), pad) <= 0)
return FALSE;
}
len = fprintf(fp
,"%" PRIu32 "\t%lu\t%lu\t%lu\t%" PRIu64 "\t%lu\t%" PRIu64 "\t%lu\t%lu\t%lu\t%lu\t"
,time_to_isoDate(stats->date)
,stats->ltoday
,stats->ttoday
,stats->uls
,stats->ulb
,stats->dls
,stats->dlb
,stats->ptoday
,stats->etoday
,stats->ftoday
,stats->nusers
);
if(len >= sizeof(pad))
return FALSE;
return fprintf(fp, "%.*s\n", sizeof(pad) - (len + 1), pad) > 0;
}
/****************************************************************************/
......@@ -86,48 +336,100 @@ ulong getposts(scfg_t* cfg, uint subnum)
return result;
}
BOOL inc_sys_upload_stats(scfg_t* cfg, ulong files, ulong bytes)
{
char str[MAX_PATH+1];
int file;
uint32_t val; // TODO: support > 4GB uploads in a day
SAFEPRINTF(str,"%sdsts.dab",cfg->ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
(void)lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
(void)read(file,&val,4); /* Uploads today */
val+=files;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
(void)read(file,&val,4); /* Upload bytes today */
val+=bytes;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
close(file);
return(TRUE);
}
BOOL inc_sys_download_stats(scfg_t* cfg, ulong files, ulong bytes)
{
char str[MAX_PATH+1];
int file;
uint32_t val; // TODO: support > 4GB downloads in a day
SAFEPRINTF(str,"%sdsts.dab",cfg->ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
(void)lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
(void)read(file,&val,4); /* Downloads today */
val+=files;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
(void)read(file,&val,4); /* Download bytes today */
val+=bytes;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
close(file);
return(TRUE);
static void inc_xfer_stat_keys(str_list_t* ini, const char* section, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
iniSetLongInt(ini, section, files_key, iniGetLongInt(*ini, section, files_key, 0) + files, /* style: */NULL);
iniSetBytes(ini, section, bytes_key, /* unit: */1, iniGetBytes(*ini, section, bytes_key, /* unit: */1, 0) + bytes, /* style: */NULL);
}
static BOOL inc_xfer_stats(scfg_t* cfg, uint node, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
inc_xfer_stat_keys(&ini, strStatsTotal, files, bytes, files_key, bytes_key);
inc_xfer_stat_keys(&ini, strStatsToday, files, bytes, files_key, bytes_key);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
static BOOL inc_all_xfer_stats(scfg_t* cfg, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_xfer_stats(cfg, cfg->node_num, files, bytes, files_key, bytes_key);
return inc_xfer_stats(cfg, /* system = node_num 0 */0, files, bytes, files_key, bytes_key) && success;
}
BOOL inc_upload_stats(scfg_t* cfg, ulong files, uint64_t bytes)
{
return inc_all_xfer_stats(cfg, files, bytes, strStatsUploads, strStatsUploadBytes);
}
BOOL inc_download_stats(scfg_t* cfg, ulong files, uint64_t bytes)
{
return inc_all_xfer_stats(cfg, files, bytes, strStatsDownloads, strStatsDownloadBytes);
}
static BOOL inc_post_stat(scfg_t* cfg, uint node, uint count)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetLongInt(&ini, strStatsToday, strStatsPosts, iniGetLongInt(ini, strStatsToday, strStatsPosts, 0) + count, /* style: */NULL);
iniSetLongInt(&ini, strStatsTotal, strStatsPosts, iniGetLongInt(ini, strStatsTotal, strStatsPosts, 0) + count, /* style: */NULL);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
BOOL inc_post_stats(scfg_t* cfg, uint count)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_post_stat(cfg, cfg->node_num, count);
return inc_post_stat(cfg, /* system = node_num 0 */0, count) && success;
}
static BOOL inc_email_stat(scfg_t* cfg, uint node, uint count, BOOL feedback)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
const char* key = feedback ? strStatsFeedback : strStatsEmail;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetLongInt(&ini, strStatsToday, key, iniGetLongInt(ini, strStatsToday, key, 0) + count, /* style: */NULL);
iniSetLongInt(&ini, strStatsTotal, key, iniGetLongInt(ini, strStatsTotal, key, 0) + count, /* style: */NULL);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
BOOL inc_email_stats(scfg_t* cfg, uint count, BOOL feedback)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_email_stat(cfg, cfg->node_num, count, feedback);
return inc_email_stat(cfg, /* system = node_num 0 */0, count, feedback) && success;
}
......@@ -20,6 +20,7 @@
#ifndef _GETSTATS_H_
#define _GETSTATS_H_
#include <stdio.h> // FILE
#include "scfgdefs.h" // scfg_t
#include "dllexport.h"
......@@ -27,13 +28,26 @@
extern "C" {
#endif
DLLEXPORT BOOL getstats(scfg_t* cfg, char node, stats_t* stats);
DLLEXPORT ulong getposts(scfg_t* cfg, uint subnum);
DLLEXPORT long getfiles(scfg_t* cfg, uint dirnum);
DLLEXPORT BOOL inc_sys_upload_stats(scfg_t*, ulong files, ulong bytes);
DLLEXPORT BOOL inc_sys_download_stats(scfg_t*, ulong files, ulong bytes);
DLLEXPORT char* dstats_fname(scfg_t*, uint node, char* path, size_t);
DLLEXPORT char* cstats_fname(scfg_t*, uint node, char* path, size_t);
DLLEXPORT FILE* fopen_dstats(scfg_t*, uint node, BOOL for_write);
DLLEXPORT FILE* fopen_cstats(scfg_t*, uint node, BOOL for_write);
DLLEXPORT BOOL fclose_cstats(FILE*);
DLLEXPORT BOOL fclose_dstats(FILE*);
DLLEXPORT BOOL fread_dstats(FILE*, stats_t*);
DLLEXPORT BOOL fwrite_dstats(FILE*, const stats_t*);
DLLEXPORT BOOL fwrite_cstats(FILE*, const stats_t*);
DLLEXPORT BOOL getstats(scfg_t*, uint node, stats_t*);
DLLEXPORT BOOL putstats(scfg_t*, uint node, const stats_t*);
DLLEXPORT ulong getposts(scfg_t*, uint subnum);
DLLEXPORT long getfiles(scfg_t*, uint dirnum);
DLLEXPORT void rolloverstats(stats_t*);
DLLEXPORT BOOL inc_post_stats(scfg_t*, uint count);
DLLEXPORT BOOL inc_email_stats(scfg_t*, uint count, BOOL feedback);
DLLEXPORT BOOL inc_upload_stats(scfg_t*, ulong files, uint64_t bytes);
DLLEXPORT BOOL inc_download_stats(scfg_t*, ulong files, uint64_t bytes);
#ifdef __cplusplus
}
#endif
#endif /* Don't add anything after this line */
\ No newline at end of file
#endif /* Don't add anything after this line */
......@@ -641,13 +641,13 @@ static JSBool js_sysstats_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
*vp=UINT_TO_JSVAL(stats.uls);