2 * Mail Rate Limiter using TBF
4 * (c) 2009 Martin Mares <mj@ucw.cz>
19 const char progname[] = "mrate";
25 typedef unsigned long long ull;
26 static double tbf_rate;
27 static double tbf_burst;
28 static double tbf_current;
29 static ull tbf_last_hit;
36 struct passwd *pw = getpwuid(getuid());
38 die("Sorry, but you don't exist!");
39 db_name = xmalloc(strlen(pw->pw_dir) + 12);
40 sprintf(db_name, "%s/.mrate.db", pw->pw_dir);
42 verb(2, "Opening database %s", db_name);
43 db_fd = open(db_name, O_RDWR | O_CREAT, 0600);
45 die("Cannot open database %s: %m", db_name);
46 if (flock(db_fd, LOCK_EX) < 0)
47 die("Cannot flock %s: %m", db_name);
50 int c = read(db_fd, buf, sizeof(buf)-1);
52 die("Cannot read %s: %m", db_name);
53 if (c >= (int)sizeof(buf)-1)
54 die("Database file %s too long -- why?", db_name);
57 if (sscanf(buf, "%llu%lf", &tbf_last_hit, &tbf_current) != 2)
62 verb(2, "Old TBF status: last_hit=%llu tokens=%f", tbf_last_hit, tbf_current);
68 verb(2, "New TBF status: last_hit=%llu tokens=%f", tbf_last_hit, tbf_current);
70 int l = sprintf(buf, "%llu %f\n", tbf_last_hit, tbf_current);
71 lseek(db_fd, 0, SEEK_SET);
72 int r = write(db_fd, buf, l);
74 die("Error writing %s: %m", db_name);
75 if (ftruncate(db_fd, l) < 0)
76 die("Error truncating %s: %m", db_name);
77 flock(db_fd, LOCK_UN);
85 if (gettimeofday(&tv, NULL) < 0)
86 die("System clock went broken: %m");
87 ull now = (ull) tv.tv_sec * 1000000 + tv.tv_usec;
88 ull delta = now - tbf_last_hit;
90 verb(2, "Time since last hit: %llu.%06u", delta/1000000, (unsigned int)(delta % 1000000));
91 tbf_current += tbf_rate * delta / 1000000;
92 if (tbf_current > tbf_burst)
93 tbf_current = tbf_burst;
96 verb(1, "Message passed");
102 verb(1, "Message dropped");
110 fprintf(stderr, "Usage: mrate [<options>] <rate> <time> [<burst>]\n\
113 -d <db>\t\tUse <db> as a database (default: ~/.mrate.db; beware of NFS)\n\
114 -v\t\tIncrease verbosity\n\
116 Maximum sustained flow of <rate> mails per <time> seconds will be allowed,\n\
117 possibly with shorts bursts of <burst> mails exceeding the limit.\n\
119 MailDups " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
120 It can be freely distributed and used according to the GNU GPL v2.\n\
126 main(int argc, char **argv)
129 while ((c = getopt(argc, argv, "a:d:l:v")) >= 0)
141 if (argc != optind+2 && argc != optind+3)
144 double rate = atof(argv[optind]);
145 double slot = atof(argv[optind+1]);
147 die("Called with <time>=0, which does not make sense");
148 tbf_rate = rate / slot;
150 tbf_burst = atof(argv[optind+2]);
152 tbf_burst = 2*tbf_rate;
155 verb(2, "TBF setup: rate=%f burst=%f", tbf_rate, tbf_burst);