48#ifndef DEFAULT_QUOTA_DB
49#error "Please define DEFAULT_QUOTA_DB preprocessor constant."
55TDB_CONTEXT *
db =
nullptr;
57#define KEY_LAST_ACTIVITY "last-activity"
58#define KEY_PERIOD_START "period-start"
59#define KEY_PERIOD_LENGTH_CONFIGURED "period-length-configured"
60#define KEY_TIME_BUDGET_LEFT "time-budget-left"
61#define KEY_TIME_BUDGET_CONFIGURED "time-budget-configured"
64#define TQ_BUFFERSIZE 1024
81 logfile = fopen(logfilename,
"a");
88static void vlog(
const char *level,
const char *format, va_list args)
92 fprintf(
logfile,
"%ld %s| %s: ",
static_cast<long int>(
now),
94 vfprintf (
logfile, format, args);
103 va_start (args, format);
104 vlog(
"DEBUG", format, args);
113 va_start (args, format);
114 vlog(
"INFO", format, args);
122 va_start (args, format);
123 vlog(
"ERROR", format, args);
131 va_start (args, format);
132 vlog(
"FATAL", format, args);
139 db = tdb_open(
db_path, 0, TDB_CLEAR_IF_FIRST, O_CREAT | O_RDWR, 0666);
151static char *
KeyString(
int &len,
const char *user_key,
const char *sub_key)
156 len = snprintf(keybuffer,
sizeof(keybuffer),
"%s-%s", user_key, sub_key);
158 log_error(
"Cannot add entry: %s-%s", user_key, sub_key);
161 }
else if (
static_cast<size_t>(len) >=
sizeof(keybuffer)) {
162 log_error(
"key too long (%s,%s)\n", user_key, sub_key);
169static void writeTime(
const char *user_key,
const char *sub_key, time_t t)
172 if (
char *keybuffer =
KeyString(len, user_key, sub_key)) {
176 key.dptr =
reinterpret_cast<unsigned char *
>(keybuffer);
179 data.dptr =
reinterpret_cast<unsigned char *
>(&t);
180 data.dsize =
sizeof(t);
182 tdb_store(
db, key, data, TDB_REPLACE);
183 log_debug(
"writeTime(\"%s\", %d)\n", keybuffer, t);
187static time_t
readTime(
const char *user_key,
const char *sub_key)
190 if (
char *keybuffer =
KeyString(len, user_key, sub_key)) {
193 key.dptr =
reinterpret_cast<unsigned char *
>(keybuffer);
196 auto data = tdb_fetch(
db, key);
199 if (data.dsize !=
sizeof(t)) {
200 log_error(
"CORRUPTED DATABASE (%s)\n", keybuffer);
202 memcpy(&t, data.dptr,
sizeof(t));
205 log_debug(
"readTime(\"%s\")=%d\n", keybuffer, t);
212static void parseTime(
const char *s, time_t *secs, time_t *start)
217 int periodLength = 3600;
221 ltime = localtime(start);
223 sscanf(s,
" %lf %c", &value, &unit);
230 *start -= ltime->tm_sec;
234 *start -= ltime->tm_min * 60 + ltime->tm_sec;
237 periodLength = 24 * 3600;
238 *start -= ltime->tm_hour * 3600 + ltime->tm_min * 60 + ltime->tm_sec;
241 periodLength = 7 * 24 * 3600;
242 *start -= ltime->tm_hour * 3600 + ltime->tm_min * 60 + ltime->tm_sec;
243 *start -= ltime->tm_wday * 24 * 3600;
247 log_error(
"Wrong time unit \"%c\". Only \"m\", \"h\", \"d\", or \"w\" allowed.\n", unit);
251 *secs = (long)(periodLength * value);
268 time_t budgetSecs, periodSecs;
271 log_info(
"reading config file \"%s\".\n", filename);
273 FH = fopen(filename,
"r");
276 unsigned int lineCount = 0;
277 while (fgets(line,
sizeof(line), FH)) {
279 if (line[0] ==
'#') {
282 if ((cp = strchr (line,
'\n')) !=
NULL) {
286 log_debug(
"read config line %u: \"%s\".\n", lineCount, line);
287 if ((username = strtok(line,
"\t ")) !=
NULL) {
290 if ((budget = strtok(
nullptr,
"/")) ==
NULL) {
291 fprintf(stderr,
"ERROR: missing 'budget' field on line %u of '%s'.\n", lineCount, filename);
294 if ((period = strtok(
nullptr,
"/")) ==
NULL) {
295 fprintf(stderr,
"ERROR: missing 'period' field on line %u of '%s'.\n", lineCount, filename);
302 log_debug(
"read time quota for user \"%s\": %lds / %lds starting %lds\n",
303 username, budgetSecs, periodSecs, start);
322 time_t activityLength;
325 time_t userPeriodLength;
326 time_t timeBudgetCurrent;
327 time_t timeBudgetConfigured;
330 log_debug(
"processActivity(\"%s\")\n", user_key);
334 if ( periodStart == 0 ) {
340 periodLength =
now - periodStart;
342 if ( userPeriodLength == 0 ) {
344 log_debug(
"No period length found for user \"%s\". Quota for this user disabled.\n", user_key);
347 if ( periodLength >= userPeriodLength ) {
349 log_debug(
"New time period started for user \"%s\".\n", user_key);
350 while ( periodStart <
now ) {
351 periodStart += periodLength;
355 if ( timeBudgetConfigured == 0 ) {
356 log_debug(
"No time budget configured for user \"%s\". Quota for this user disabled.\n", user_key);
366 if ( lastActivity == 0 ) {
370 activityLength =
now - lastActivity;
373 log_debug(
"Activity pause detected for user \"%s\".\n", user_key);
379 log_debug(
"Time budget reduced by %ld for user \"%s\".\n",
380 activityLength, user_key);
382 timeBudgetCurrent -= activityLength;
388 snprintf(message,
TQ_BUFFERSIZE,
"message=\"Remaining quota for '%s' is %d seconds.\"", user_key, (
int)timeBudgetCurrent);
389 if ( timeBudgetCurrent > 0 ) {
400 log_error(
"Wrong usage. Please reconfigure in squid.conf.\n");
402 fprintf(stderr,
"Usage: %s [-d] [-l logfile] [-b dbpath] [-p pauselen] [-h] configfile\n",
program_name);
403 fprintf(stderr,
" -d enable debugging output to logfile\n");
404 fprintf(stderr,
" -l logfile log messages to logfile\n");
405 fprintf(stderr,
" -b dbpath Path where persistent session database will be kept\n");
406 fprintf(stderr,
" If option is not used, then " DEFAULT_QUOTA_DB
" will be used.\n");
407 fprintf(stderr,
" -p pauselen length in seconds to describe a pause between 2 requests.\n");
408 fprintf(stderr,
" -h show show command line help.\n");
409 fprintf(stderr,
"configfile is a file containing time quota definitions.\n");
419 while ((opt =
getopt(argc, argv,
"dp:l:b:h")) != -1) {
440 log_info(
"Starting %s\n", __FILE__);
441 setbuf(stdout,
nullptr);
445 if (
optind + 1 != argc ) {
452 log_info(
"Waiting for requests...\n");
455 const char *user_key = strtok(request,
" \n");
#define HELPER_INPUT_BUFFER
#define KEY_TIME_BUDGET_LEFT
static void log_error(const char *format,...)
static void shutdown_db(void)
int main(int argc, char **argv)
static void log_debug(const char *format,...)
static void processActivity(const char *user_key)
static void writeTime(const char *user_key, const char *sub_key, time_t t)
static void init_db(void)
static time_t readTime(const char *user_key, const char *sub_key)
static void parseTime(const char *s, time_t *secs, time_t *start)
#define KEY_PERIOD_LENGTH_CONFIGURED
static void open_log(const char *logfilename)
static void vlog(const char *level, const char *format, va_list args)
#define KEY_LAST_ACTIVITY
static void log_info(const char *format,...)
static int tq_debug_enabled
static void readConfig(const char *filename)
static char * KeyString(int &len, const char *user_key, const char *sub_key)
static void log_fatal(const char *format,...)
const char * program_name
#define KEY_TIME_BUDGET_CONFIGURED
int getopt(int nargc, char *const *nargv, const char *ostr)