2 * Mail Rate Limiter using TBF
4 * (c) 2009 Martin Mares <mj@ucw.cz>
23 typedef unsigned long long ull;
24 static double tbf_rate;
25 static double tbf_burst;
26 static double tbf_current;
27 static ull tbf_last_hit;
34 struct passwd *pw = getpwuid(getuid());
36 die("Sorry, but you don't exist!");
37 db_name = xmalloc(strlen(pw->pw_dir) + 12);
38 sprintf(db_name, "%s/.mrate.db", pw->pw_dir);
40 verb(2, "Opening database %s", db_name);
41 db_fd = open(db_name, O_RDWR | O_CREAT, 0600);
43 die("Cannot open database %s: %m", db_name);
44 if (flock(db_fd, LOCK_EX) < 0)
45 die("Cannot flock %s: %m", db_name);
48 int c = read(db_fd, buf, sizeof(buf)-1);
50 die("Cannot read %s: %m", db_name);
51 if (c >= (int)sizeof(buf)-1)
52 die("Database file %s too long -- why?", db_name);
55 if (sscanf(buf, "%llu%lf", &tbf_last_hit, &tbf_current) != 2)
60 verb(2, "Old TBF status: last_hit=%llu tokens=%f", tbf_last_hit, tbf_current);
66 verb(2, "New TBF status: last_hit=%llu tokens=%f", tbf_last_hit, tbf_current);
68 int l = sprintf(buf, "%llu %f\n", tbf_last_hit, tbf_current);
69 lseek(db_fd, 0, SEEK_SET);
70 int r = write(db_fd, buf, l);
72 die("Error writing %s: %m", db_name);
73 if (ftruncate(db_fd, l) < 0)
74 die("Error truncating %s: %m", db_name);
75 flock(db_fd, LOCK_UN);
83 if (gettimeofday(&tv, NULL) < 0)
84 die("System clock went broken: %m");
85 ull now = (ull) tv.tv_sec * 1000000 + tv.tv_usec;
86 ull delta = now - tbf_last_hit;
88 verb(2, "Time since last hit: %llu.%06u", delta/1000000, (unsigned int)(delta % 1000000));
89 tbf_current += tbf_rate * delta / 1000000;
90 if (tbf_current > tbf_burst)
91 tbf_current = tbf_burst;
94 verb(1, "Message passed");
100 verb(0, "Message dropped");
108 fprintf(stderr, "Usage: mrate [<options>] <rate> <time> [<burst>]\n\
111 -d <db>\t\tUse <db> as a database (default: ~/.mrate.db; beware of NFS)\n\
112 -v\t\tIncrease verbosity\n\
114 Maximum sustained flow of <rate> mails per <time> seconds will be allowed,\n\
115 possibly with shorts bursts of <burst> mails exceeding the limit.\n\
117 MailDups " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
118 It can be freely distributed and used according to the GNU GPL v2.\n\
124 main(int argc, char **argv)
127 while ((c = getopt(argc, argv, "a:d:l:v")) >= 0)
139 if (argc != optind+2 && argc != optind+3)
142 double rate = atof(argv[optind]);
143 double slot = atof(argv[optind+1]);
145 die("Called with <time>=0, which does not make sense");
146 tbf_rate = rate / slot;
148 tbf_burst = atof(argv[optind+2]);
150 tbf_burst = 2*tbf_rate;
153 verb(2, "TBF setup: rate=%f burst=%f", tbf_rate, tbf_burst);