r50309 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r50308‎ | r50309 | r50310 >
Date:15:43, 7 May 2009
Author:river
Status:deferred
Tags:
Comment:
- 1.7
- support FreeBSD
Modified paths:
  • /trunk/tools/slayerd/Makefile (modified) (history)
  • /trunk/tools/slayerd/config.cc (added) (history)
  • /trunk/tools/slayerd/config.h (added) (history)
  • /trunk/tools/slayerd/proc_freebsd.cc (added) (history)
  • /trunk/tools/slayerd/proc_linux.cc (modified) (history)
  • /trunk/tools/slayerd/process.cc (modified) (history)
  • /trunk/tools/slayerd/process.h (modified) (history)
  • /trunk/tools/slayerd/slayerd.cc (modified) (history)
  • /trunk/tools/slayerd/util.cc (added) (history)
  • /trunk/tools/slayerd/util.h (added) (history)

Diff [purge]

Index: trunk/tools/slayerd/proc_freebsd.cc
@@ -0,0 +1,82 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/stat.h>
 13+#include <sys/user.h>
 14+#include <sys/param.h>
 15+#include <sys/sysctl.h>
 16+#include <unistd.h>
 17+#include <kvm.h>
 18+#include <fcntl.h>
 19+
 20+#include <string>
 21+#include <iostream>
 22+#include <fstream>
 23+#include <boost/shared_ptr.hpp>
 24+#include <boost/filesystem.hpp>
 25+#include <boost/lexical_cast.hpp>
 26+
 27+#include "process.h"
 28+
 29+struct process_freebsd : process {
 30+ pid_t pid() const { return _pid; }
 31+ std::string command() const { return _comm; }
 32+ std::string cmdline() const { return _fullcomm; }
 33+ std::size_t rss() const { return _rss; }
 34+ std::size_t vsize() const { return _vsize; }
 35+ uid_t uid() const { return _uid; }
 36+
 37+ pid_t _pid;
 38+ std::string _comm;
 39+ std::string _fullcomm;
 40+ std::size_t _vsize;
 41+ uid_t _uid;
 42+ std::size_t _rss;
 43+};
 44+
 45+std::vector<process::pointer>
 46+enumerate_processes()
 47+{
 48+ kvm_t *kvm;
 49+ int nprocs;
 50+ struct kinfo_proc *procs;
 51+ std::vector<process::pointer> processes;
 52+ int pagesz = sysconf(_SC_PAGE_SIZE);
 53+
 54+ if ((kvm = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
 55+ throw std::runtime_error("kvm_open failed");
 56+
 57+ procs = kvm_getprocs(kvm, KERN_PROC_PROC, 0, &nprocs);
 58+ for (int i = 0; i < nprocs; ++i) {
 59+ process_freebsd *p = new process_freebsd;
 60+
 61+ char **argv = kvm_getargv(kvm, &procs[i], 2048);
 62+ if (!argv) {
 63+ delete p;
 64+ continue;
 65+ } else {
 66+ for (std::size_t i = 0; argv[i]; ++i) {
 67+ p->_fullcomm += argv[i];
 68+ p->_fullcomm += " ";
 69+ }
 70+ }
 71+
 72+ p->_pid = procs[i].ki_pid;
 73+ p->_uid = procs[i].ki_uid;
 74+ p->_vsize = procs[i].ki_size;
 75+ p->_rss = procs[i].ki_rssize * pagesz;
 76+ p->_comm = procs[i].ki_comm;
 77+
 78+ processes.push_back(process::pointer(p));
 79+ }
 80+
 81+ kvm_close(kvm);
 82+ return processes;
 83+}
Property changes on: trunk/tools/slayerd/proc_freebsd.cc
___________________________________________________________________
Added: svn:keywords
184 + Id Revision
Index: trunk/tools/slayerd/util.cc
@@ -0,0 +1,153 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/wait.h>
 13+#include <pwd.h>
 14+#include <syslog.h>
 15+
 16+#include <cerrno>
 17+#include <cstring>
 18+#include <iostream>
 19+#include <map>
 20+#include <fstream>
 21+#include <boost/format.hpp>
 22+#include <boost/lexical_cast.hpp>
 23+
 24+#include "util.h"
 25+#include "config.h"
 26+
 27+std::string
 28+username(uid_t uid)
 29+{
 30+ struct passwd *p;
 31+ if ((p = getpwuid(uid)) == 0)
 32+ return boost::lexical_cast<std::string>(uid);
 33+ return std::string(p->pw_name);
 34+}
 35+
 36+uid_t
 37+uid(std::string const &username)
 38+{
 39+ struct passwd *p;
 40+ if ((p = getpwnam(username.c_str())) == 0)
 41+ return -1;
 42+ return p->pw_uid;
 43+}
 44+
 45+void
 46+sendmail(std::string const &username, std::string const &message)
 47+{
 48+ char const *const args[] = {
 49+ config.sendmail.c_str(),
 50+ "-oi",
 51+ "-bm",
 52+ "--",
 53+ username.c_str(),
 54+ 0
 55+ };
 56+ int fds[2];
 57+ pid_t pid;
 58+
 59+ if (pipe(fds) == -1) {
 60+ log(str(boost::format("while sending mail: pipe: %s") %
 61+ std::strerror(errno)));
 62+ return;
 63+ }
 64+
 65+ switch (pid = fork()) {
 66+ case 0:
 67+ if (dup2(fds[0], 0) == -1) {
 68+ log(str(boost::format("mail child: dup2: %s") %
 69+ std::strerror(errno)));
 70+ _exit(1);
 71+ }
 72+
 73+ close(fds[0]);
 74+ close(fds[1]);
 75+ if (execv(config.sendmail.c_str(), const_cast<char *const
 76+ *>(args)) == -1)
 77+ log(str(boost::format("mail child: execv: %s") %
 78+ std::strerror(errno)));
 79+ _exit(1);
 80+
 81+ case -1:
 82+ log(str(boost::format("sending mail: fork: %s") %
 83+ std::strerror(errno)));
 84+ return;
 85+
 86+ default:
 87+ close(fds[0]);
 88+ write(fds[1], message.data(), message.size());
 89+ close(fds[1]);
 90+ }
 91+
 92+ int status;
 93+ wait(&status);
 94+ if (WIFEXITED(status)) {
 95+ int ret = WEXITSTATUS(status);
 96+ if (ret != 0)
 97+ log(str(boost::format("sending mail: child exited with status %d") % ret));
 98+ } else if (WIFSIGNALED(status)) {
 99+ log(str(boost::format("sending mail: child exited with signal %d") % WTERMSIG(status)));
 100+ }
 101+}
 102+
 103+void
 104+log(std::string const &m)
 105+{
 106+ if (config.debug)
 107+ std::cerr << m << '\n';
 108+ else
 109+ syslog(LOG_NOTICE, "%s", m.c_str());
 110+}
 111+
 112+std::string
 113+replace_file(std::string const &filename, std::map<std::string, std::string> const &vars)
 114+{
 115+ std::string file;
 116+ std::string result;
 117+
 118+ std::ifstream f(filename.c_str());
 119+ if (!f) {
 120+ log(str(boost::format("cannot open %s: %s") % filename % std::strerror(errno)));
 121+ return "";
 122+ }
 123+
 124+ std::string line;
 125+ while (std::getline(f, line)) {
 126+ std::string::size_type i, j;
 127+ while ((i = line.find('%')) != std::string::npos) {
 128+ if ((j = line.find('%', i + 1)) == std::string::npos) {
 129+ result += line;
 130+ line = "";
 131+ break;
 132+ }
 133+
 134+ result += line.substr(0, i);
 135+ std::string var = line.substr(i + 1, j - i - 1);
 136+ std::map<std::string, std::string>::const_iterator it = vars.find(var);
 137+
 138+ if (it == vars.end()) {
 139+ result += line.substr(i);
 140+ line = "";
 141+ break;
 142+ }
 143+
 144+ result += it->second;
 145+ line = line.substr(j + 1);
 146+ }
 147+
 148+ result += line;
 149+ result += '\n';
 150+ }
 151+
 152+ return result;
 153+}
 154+
Property changes on: trunk/tools/slayerd/util.cc
___________________________________________________________________
Added: svn:keywords
1155 + Id Revision
Index: trunk/tools/slayerd/process.cc
@@ -6,8 +6,10 @@
77 * warranty.
88 */
99
10 -#ifdef __linux__
 10+#if defined(__linux__)
1111 # include "proc_linux.cc"
 12+#elif defined(__FreeBSD__)
 13+# include "proc_freebsd.cc"
1214 #else
1315 # error dont know how to enumerate processes on this platform
1416 #endif
Property changes on: trunk/tools/slayerd/process.cc
___________________________________________________________________
Added: svn:keywords
1517 + Id Revision
Index: trunk/tools/slayerd/util.h
@@ -0,0 +1,24 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef UTIL_H
 13+#define UTIL_H
 14+
 15+#include <sys/types.h>
 16+#include <string>
 17+#include <map>
 18+
 19+std::string username(uid_t uid);
 20+uid_t uid(std::string const &username);
 21+void sendmail(std::string const &username, std::string const &message);
 22+void log(std::string const &m);
 23+std::string replace_file(std::string const &filename, std::map<std::string, std::string> const &vars);
 24+
 25+#endif /* !UTIL_H */
Property changes on: trunk/tools/slayerd/util.h
___________________________________________________________________
Added: svn:keywords
126 + Id Revision
Index: trunk/tools/slayerd/process.h
@@ -11,7 +11,9 @@
1212 #ifndef PROCESS_H
1313 #define PROCESS_H
1414
 15+#include <sys/types.h>
1516 #include <vector>
 17+#include <boost/shared_ptr.hpp>
1618
1719 struct process {
1820 typedef boost::shared_ptr<process> pointer;
Property changes on: trunk/tools/slayerd/process.h
___________________________________________________________________
Added: svn:keywords
1921 + Id Revision
Index: trunk/tools/slayerd/proc_linux.cc
@@ -71,6 +71,7 @@
7272 unsigned long _rt_priority, _policy;
7373 int _mshare, _mtext, _mlib, _mdata;
7474 pid_t _ppid, _pgrp, _sid, _tty, _tpgid, _state, _msize, _nice;
 75+ int pagesz = sysconf(_SC_PAGE_SIZE);
7576
7677 {
7778 std::ifstream f((pth / "stat").native_file_string().c_str());
@@ -109,6 +110,14 @@
110111 if (!std::getline(f, _fullcomm))
111112 throw std::runtime_error("could not parse cmdline");
112113 }
 114+
 115+ _mres *= pagesz;
 116+ /* command is (%s) formatted, strip parentheses. */
 117+ _comm = _comm.substr(1);
 118+ _comm.resize(_comm.size() - 1);
 119+
 120+ /* arguments are \0 separated, use spaces for display */
 121+ std::replace(_fullcomm.begin(), _fullcomm.end(), '\0', ' ');
113122 }
114123
115124 template<typename C>
Property changes on: trunk/tools/slayerd/proc_linux.cc
___________________________________________________________________
Added: svn:keywords
116125 + Id Revision
Index: trunk/tools/slayerd/slayerd.cc
@@ -12,81 +12,27 @@
1313 * slayerd: monitor user activity and regulate users using too much RAM.
1414 */
1515
16 -#include <string>
17 -#include <iostream>
18 -#include <fstream>
19 -#include <algorithm>
20 -#include <stdexcept>
21 -#include <vector>
22 -#include <map>
23 -#include <set>
24 -#include <cerrno>
25 -#include <cstring>
26 -
27 -#include <sys/types.h>
28 -#include <sys/mman.h>
29 -#include <sys/stat.h>
3016 #include <sys/wait.h>
31 -#include <unistd.h>
32 -#include <pwd.h>
33 -#include <signal.h>
 17+#include <sys/mman.h>
3418 #include <syslog.h>
3519
36 -#include <boost/filesystem/path.hpp>
37 -#include <boost/filesystem/operations.hpp>
38 -#include <boost/lexical_cast.hpp>
 20+#include <cerrno>
 21+#include <cstring>
 22+#include <csignal>
 23+#include <iostream>
 24+#include <map>
 25+#include <fstream>
3926 #include <boost/format.hpp>
40 -#include <boost/shared_ptr.hpp>
 27+#include <boost/lexical_cast.hpp>
4128
4229 #include "process.h"
 30+#include "config.h"
 31+#include "util.h"
4332
44 -namespace fs = boost::filesystem;
45 -
4633 namespace {
4734 std::string CONFFILE = "/etc/slayerd/slayerd.conf";
4835 }
4936
50 -struct config_t {
51 - std::size_t limit;
52 - std::size_t thresh;
53 - int delay;
54 - std::string mailmessage;
55 - std::string sendmail;
56 - std::string pidfile;
57 - std::set<uid_t> exempt;
58 - int minuid;
59 - bool debug;
60 -
61 - config_t()
62 - : limit(0)
63 - , minuid(-1)
64 - , thresh(0)
65 - , delay(60)
66 - , mailmessage(ETCDIR "/mailmessage")
67 - , sendmail("/usr/lib/sendmail -oi -bm --")
68 - , pidfile("/var/run/slayerd.pid")
69 - , debug(false)
70 - {}
71 -} config;
72 -
73 -std::string
74 -username(uid_t uid)
75 -{
76 - struct passwd *p;
77 - if ((p = getpwuid(uid)) == 0)
78 - return boost::lexical_cast<std::string>(uid);
79 - return std::string(p->pw_name);
80 -}
81 -
82 -uid_t
83 -uid(std::string const &username)
84 -{
85 - struct passwd *p;
86 - if ((p = getpwnam(username.c_str())) == 0)
87 - return -1;
88 - return p->pw_uid;
89 -}
90 -
9137 struct user {
9238 user() : uid(-1), rss(0) {}
9339
@@ -95,23 +41,9 @@
9642 std::vector<process::pointer> processes;
9743 };
9844
99 -/*
100 - * A sort comparator that uses a particular struct field.
101 - */
102 -template<typename S, typename T, T (S::*F)>
103 -bool
104 -field_comparator(S const &a, S const &b)
105 -{
106 - return b.*F < a.*F;
107 -}
 45+void run_once();
 46+void check_user(user &u);
10847
109 -template<typename S, typename T, T (S::*F)(void) const>
110 -bool
111 -ptmf_comparator(boost::shared_ptr<S> const &a, boost::shared_ptr<S> const &b)
112 -{
113 - return ((*b).*F)() < ((*a).*F)();
114 -}
115 -
11648 void
11749 version(void) {
11850 std::cerr << "slayerd " << VERSION << "\n";
@@ -125,222 +57,6 @@
12658 }
12759
12860 void
129 -log(std::string const &m)
130 -{
131 - if (config.debug)
132 - std::cerr << m << '\n';
133 - else
134 - syslog(LOG_NOTICE, "%s", m.c_str());
135 -}
136 -
137 -void
138 -sendmail(std::string const &username, std::string const &message)
139 -{
140 - char const *const args[] = {
141 - config.sendmail.c_str(),
142 - "-oi",
143 - "-bm",
144 - "--",
145 - username.c_str(),
146 - 0
147 - };
148 - int fds[2];
149 - pid_t pid;
150 -
151 - if (pipe(fds) == -1) {
152 - log(str(boost::format("while sending mail: pipe: %s") %
153 - std::strerror(errno)));
154 - return;
155 - }
156 -
157 - switch (pid = fork()) {
158 - case 0:
159 - if (dup2(fds[0], 0) == -1) {
160 - log(str(boost::format("mail child: dup2: %s") %
161 - std::strerror(errno)));
162 - _exit(1);
163 - }
164 -
165 - close(fds[0]);
166 - close(fds[1]);
167 - if (execv(config.sendmail.c_str(), const_cast<char *const
168 - *>(args)) == -1)
169 - log(str(boost::format("mail child: execv: %s") %
170 - std::strerror(errno)));
171 - _exit(1);
172 -
173 - case -1:
174 - log(str(boost::format("sending mail: fork: %s") %
175 - std::strerror(errno)));
176 - return;
177 -
178 - default:
179 - close(fds[0]);
180 - write(fds[1], message.data(), message.size());
181 - close(fds[1]);
182 - }
183 -
184 - int status;
185 - wait(&status);
186 - if (WIFEXITED(status)) {
187 - int ret = WEXITSTATUS(status);
188 - if (ret != 0)
189 - log(str(boost::format("sending mail: child exited with status %d") % ret));
190 - } else if (WIFSIGNALED(status)) {
191 - log(str(boost::format("sending mail: child exited with signal %d") % WTERMSIG(status)));
192 - }
193 -}
194 -
195 -std::string
196 -replace_file(std::string const &filename, std::map<std::string, std::string> const &vars)
197 -{
198 - std::string file;
199 - std::string result;
200 -
201 - std::ifstream f(filename.c_str());
202 - if (!f) {
203 - log(str(boost::format("cannot open %s: %s") % filename % std::strerror(errno)));
204 - return "";
205 - }
206 -
207 - std::string line;
208 - while (std::getline(f, line)) {
209 - std::string::size_type i, j;
210 - while ((i = line.find('%')) != std::string::npos) {
211 - if ((j = line.find('%', i + 1)) == std::string::npos) {
212 - result += line;
213 - line = "";
214 - break;
215 - }
216 -
217 - result += line.substr(0, i);
218 - std::string var = line.substr(i + 1, j - i - 1);
219 - std::map<std::string, std::string>::const_iterator it = vars.find(var);
220 -
221 - if (it == vars.end()) {
222 - result += line.substr(i);
223 - line = "";
224 - break;
225 - }
226 -
227 - result += it->second;
228 - line = line.substr(j + 1);
229 - }
230 -
231 - result += line;
232 - result += '\n';
233 - }
234 -
235 - return result;
236 -}
237 -
238 -bool
239 -configure(void)
240 -{
241 - std::ifstream conffile(CONFFILE.c_str());
242 - if (!conffile) {
243 - std::string err = str(boost::format("cannot open %s: %s") % CONFFILE % std::strerror(errno));
244 - std::cerr << err << '\n';
245 - log(err);
246 - return false;
247 - }
248 -
249 - std::string line;
250 - int lineno = 0;
251 - while (std::getline(conffile, line)) {
252 - ++lineno;
253 -
254 - if (line[0] == '#')
255 - continue;
256 - if (line.empty())
257 - continue;
258 -
259 - std::istringstream l(line);
260 - std::string d;
261 - if (!(l >> d))
262 - continue;
263 -
264 - if (d == "limit") {
265 - if (!(l >> config.limit)) {
266 - std::string err = str(boost::format("\"%s\", line %d: invalid limit") % CONFFILE % lineno);
267 - std::cerr << err << '\n';
268 - log(err);
269 - return false;
270 - }
271 - config.limit *= 1024 * 1024;
272 - } else if (d == "thresh") {
273 - if (!(l >> config.thresh)) {
274 - std::string err = str(boost::format("\"%s\", line %d: invalid threshold") % CONFFILE % lineno);
275 - std::cerr << err << '\n';
276 - log(err);
277 - return false;
278 - }
279 - config.thresh *= 1024 * 1024;
280 - } else if (d == "exempt") {
281 - std::string username;
282 - if (!(l >> username)) {
283 - std::string err = str(boost::format("\"%s\", line %d: invalid username") % CONFFILE % lineno);
284 - std::cerr << err << '\n';
285 - log(err);
286 - return false;
287 - }
288 -
289 - uid_t id = uid(username);
290 - if (id == -1) {
291 - std::string err = str(boost::format("\"%s\", line %d: user \"%s\" does not exist")
292 - % CONFFILE % lineno % username);
293 - std::cerr << err << '\n';
294 - log(err);
295 - return false;
296 - }
297 - config.exempt.insert(id);
298 - } else if (d == "delay") {
299 - if (!(l >> config.delay)) {
300 - std::string err = str(boost::format("\"%s\", line %d: invalid delay") % CONFFILE % lineno);
301 - std::cerr << err << '\n';
302 - log(err);
303 - return false;
304 - }
305 - } else if (d == "mailmessage") {
306 - if (!(l >> config.mailmessage)) {
307 - std::string err = str(boost::format("\"%s\", line %d: invalid mail message") % CONFFILE % lineno);
308 - std::cerr << err << '\n';
309 - log(err);
310 - return false;
311 - }
312 - } else if (d == "sendmail") {
313 - if (!(l >> config.sendmail)) {
314 - std::string err = str(boost::format("\"%s\", line %d: invalid sendmail command") % CONFFILE % lineno);
315 - std::cerr << err << '\n';
316 - log(err);
317 - return false;
318 - }
319 - } else if (d == "pidfile") {
320 - if (!(l >> config.pidfile)) {
321 - std::string err = str(boost::format("\"%s\", line %d: invalid pid file") % CONFFILE % lineno);
322 - std::cerr << err << '\n';
323 - log(err);
324 - return false;
325 - }
326 - } else if (d == "minuid") {
327 - if (!(l >> config.minuid) || config.minuid < 0) {
328 - std::string err = str(boost::format("\"%s\", line %d: invalid minimum uid") % CONFFILE % lineno);
329 - std::cerr << err << '\n';
330 - log(err);
331 - return false;
332 - }
333 - } else {
334 - std::string err = str(boost::format("\"%s\", line %d: invalid directive") % CONFFILE % lineno);
335 - std::cerr << err << '\n';
336 - log(err);
337 - return false;
338 - }
339 - }
340 -
341 - return true;
342 -}
343 -
344 -void
34561 rmpidfile(void)
34662 {
34763 unlink(config.pidfile.c_str());
@@ -384,14 +100,11 @@
385101 int
386102 main(int argc, char **argv)
387103 {
388 - int pagesize = sysconf(_SC_PAGE_SIZE), fflag = 0;
 104+ int fflag = 0;
389105 int c;
390106
391107 config.exempt.insert(0); /* root is always exempt */
392108
393 - char nodename[255];
394 - gethostname(nodename, sizeof nodename);
395 -
396109 while ((c = getopt(argc, argv, "fvhc:D")) != -1) {
397110 switch (c) {
398111 case 'f':
@@ -428,7 +141,7 @@
429142 if (!config.debug)
430143 openlog("slayerd", LOG_PID, LOG_DAEMON);
431144
432 - if (!configure())
 145+ if (!configure(CONFFILE))
433146 return 1;
434147
435148 if (config.limit == 0 || config.thresh == 0) {
@@ -451,106 +164,115 @@
452165 % config.delay % (config.limit / 1024 / 1024) % (config.thresh / 1024 / 1024)));
453166
454167 for (;;) {
455 - std::vector<process::pointer> processes(enumerate_processes());
 168+ run_once();
 169+ sleep(config.delay);
 170+ }
 171+}
456172
457 - /*
458 - * Aggregate the processes by user.
459 - */
460 - std::vector<user> users;
461 - for (std::size_t i = 0, end = processes.size(); i < end; ++i) {
462 - process::pointer p = processes[i];
463 - user *u = 0;
 173+struct process_sort_rss {
 174+ bool operator() (process::pointer a, process::pointer b) const {
 175+ return a->rss() > b->rss();
 176+ }
 177+};
464178
465 - for (std::size_t ui = 0, uend = users.size(); ui != uend; ++ui)
466 - if (users[ui].uid == p->uid()) {
467 - u = &users[ui];
468 - break;
469 - }
 179+void
 180+run_once()
 181+{
 182+ std::vector<process::pointer> processes(enumerate_processes());
470183
471 - if (u == 0) {
472 - std::size_t n = users.size();
473 - users.resize(n + 1);
474 - users[n].uid = p->uid();
475 - u = &users[n];
 184+ /*
 185+ * Aggregate the processes by user.
 186+ */
 187+ std::vector<user> users;
 188+ for (std::size_t i = 0, end = processes.size(); i < end; ++i) {
 189+ process::pointer p = processes[i];
 190+ user *u = 0;
 191+
 192+ for (std::size_t ui = 0, uend = users.size(); ui != uend; ++ui)
 193+ if (users[ui].uid == p->uid()) {
 194+ u = &users[ui];
 195+ break;
476196 }
477197
478 - u->rss += p->rss();
479 - u->processes.push_back(p);
 198+ if (u == 0) {
 199+ std::size_t n = users.size();
 200+ users.resize(n + 1);
 201+ users[n].uid = p->uid();
 202+ u = &users[n];
480203 }
481204
482 - /*
483 - * Sort user by RSS.
484 - */
485 - std::sort(users.begin(), users.end(), field_comparator<user, std::size_t, &user::rss>);
 205+ u->rss += p->rss();
 206+ u->processes.push_back(p);
 207+ }
486208
487 - for (std::size_t i = 0, end = users.size(); i < end; ++i) {
488 - user &u = users[i];
489 - std::size_t bytes = std::size_t(u.rss) * pagesize;
 209+ for (std::size_t i = 0, end = users.size(); i < end; ++i) {
 210+ check_user(users[i]);
 211+ }
 212+}
490213
491 - if (config.exempt.find(u.uid) != config.exempt.end())
492 - continue;
 214+void
 215+check_user(user &u)
 216+{
 217+ std::size_t bytes = u.rss;
493218
494 - if (config.minuid >= 0 && u.uid < config.minuid)
495 - continue;
 219+ if (config.exempt.find(u.uid) != config.exempt.end())
 220+ return;
496221
497 - if (bytes < config.limit)
498 - continue;
 222+ if (config.minuid >= 0 && u.uid < config.minuid)
 223+ return;
499224
500 - std::string uname = username(u.uid);
501 - std::string process_list;
 225+ if (bytes < config.limit)
 226+ return;
502227
503 - log(str(boost::format("user \"%s\" is using %dM, over configured limit %dM")
504 - % uname
505 - % (bytes / 1024 / 1024)
506 - % (config.limit / 1024 / 1024)));
 228+ std::string uname = username(u.uid);
 229+ std::string process_list;
507230
508 - std::sort(u.processes.begin(), u.processes.end(), ptmf_comparator<process, std::size_t, &process::rss>);
 231+ log(str(boost::format("user \"%s\" is using %dM, over configured limit %dM")
 232+ % uname
 233+ % (bytes / 1024 / 1024)
 234+ % (config.limit / 1024 / 1024)));
509235
510 - while (bytes >= config.thresh && !u.processes.empty()) {
511 - process::pointer p = u.processes[0];
 236+ std::sort(u.processes.begin(), u.processes.end(), process_sort_rss());
512237
513 - /* command is (%s) formatted, strip parentheses. */
514 - std::string comm = p->command().substr(1);
515 - comm.resize(comm.size() - 1);
 238+ while (bytes >= config.thresh && !u.processes.empty()) {
 239+ process::pointer p = u.processes[0];
516240
517 - /* arguments are \0 separated, use spaces for display */
518 - std::string cm = p->cmdline();
519 - std::replace(cm.begin(), cm.end(), '\0', ' ');
 241+ std::string const &comm = p->command();
 242+ std::string const &cm = p->cmdline();
520243
521 - if (!config.debug)
522 - kill(p->pid(), SIGKILL);
 244+ if (!config.debug)
 245+ kill(p->pid(), SIGKILL);
523246
524 - std::size_t thissize = std::size_t(p->rss()) * pagesize;
 247+ std::size_t thissize = std::size_t(p->rss());
525248
526 - log(str(boost::format(" killed process \"%s\" (pid %d) using %dM, usage now %dM")
527 - % comm % p->pid()
528 - % (thissize / 1024 / 1024)
529 - % ((bytes - thissize) / 1024 / 1024)));
 249+ log(str(boost::format(" killed process \"%s\" (pid %d) using %dM, usage now %dM")
 250+ % comm % p->pid()
 251+ % (thissize / 1024 / 1024)
 252+ % ((bytes - thissize) / 1024 / 1024)));
530253
531 - process_list += str(boost::format(" %s (pid %d), using %d megabyte(s).\n"
532 - " command: %s\n")
533 - % comm % p->pid() % (thissize / 1024 / 1024) % p->cmdline());
 254+ process_list += str(boost::format(" %s (pid %d), using %d megabyte(s).\n"
 255+ " command: %s\n")
 256+ % comm % p->pid() % (thissize / 1024 / 1024) % p->cmdline());
534257
535 - bytes -= thissize;
536 - u.processes.erase(u.processes.begin());
537 - }
 258+ bytes -= thissize;
 259+ u.processes.erase(u.processes.begin());
 260+ }
538261
539 - log(str(boost::format(" usage is now within acceptable limits (%dM)")
540 - % (bytes / 1024 / 1024)));
 262+ log(str(boost::format(" usage is now within acceptable limits (%dM)")
 263+ % (bytes / 1024 / 1024)));
541264
542 - std::map<std::string, std::string> msgvars;
543 - msgvars["limit"] = boost::lexical_cast<std::string>(config.limit / 1024 / 1024);
544 - msgvars["thresh"] = boost::lexical_cast<std::string>(config.thresh / 1024 / 1024);
545 - msgvars["user"] = uname;
546 - msgvars["hostname"] = nodename;
547 - msgvars["current"] = boost::lexical_cast<std::string>(bytes / 1024 / 1024);
548 - msgvars["processes"] = process_list;
 265+ char nodename[255];
 266+ gethostname(nodename, sizeof nodename);
549267
550 - std::string message = replace_file(config.mailmessage, msgvars);
551 - if (!config.debug && !message.empty())
552 - sendmail(uname, message);
553 - }
 268+ std::map<std::string, std::string> msgvars;
 269+ msgvars["limit"] = boost::lexical_cast<std::string>(config.limit / 1024 / 1024);
 270+ msgvars["thresh"] = boost::lexical_cast<std::string>(config.thresh / 1024 / 1024);
 271+ msgvars["user"] = uname;
 272+ msgvars["hostname"] = nodename;
 273+ msgvars["current"] = boost::lexical_cast<std::string>(bytes / 1024 / 1024);
 274+ msgvars["processes"] = process_list;
554275
555 - sleep(config.delay);
556 - }
 276+ std::string message = replace_file(config.mailmessage, msgvars);
 277+ if (!config.debug && !message.empty())
 278+ sendmail(uname, message);
557279 }
Index: trunk/tools/slayerd/config.cc
@@ -0,0 +1,117 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <iostream>
 13+#include <fstream>
 14+#include <cerrno>
 15+#include <cstring>
 16+#include <sstream>
 17+#include <boost/format.hpp>
 18+
 19+#include "config.h"
 20+#include "util.h"
 21+
 22+config_t config;
 23+
 24+bool
 25+configure(std::string const &file)
 26+{
 27+ std::ifstream conffile(file.c_str());
 28+ if (!conffile) {
 29+ std::string err = str(boost::format("cannot open %s: %s") % file % std::strerror(errno));
 30+ std::cerr << err << '\n';
 31+ return false;
 32+ }
 33+
 34+ std::string line;
 35+ int lineno = 0;
 36+ while (std::getline(conffile, line)) {
 37+ ++lineno;
 38+
 39+ if (line[0] == '#')
 40+ continue;
 41+ if (line.empty())
 42+ continue;
 43+
 44+ std::istringstream l(line);
 45+ std::string d;
 46+ if (!(l >> d))
 47+ continue;
 48+
 49+ if (d == "limit") {
 50+ if (!(l >> config.limit)) {
 51+ std::string err = str(boost::format("\"%s\", line %d: invalid limit") % file % lineno);
 52+ std::cerr << err << '\n';
 53+ return false;
 54+ }
 55+ config.limit *= 1024 * 1024;
 56+ } else if (d == "thresh") {
 57+ if (!(l >> config.thresh)) {
 58+ std::string err = str(boost::format("\"%s\", line %d: invalid threshold") % file % lineno);
 59+ std::cerr << err << '\n';
 60+ return false;
 61+ }
 62+ config.thresh *= 1024 * 1024;
 63+ } else if (d == "exempt") {
 64+ std::string username;
 65+ if (!(l >> username)) {
 66+ std::string err = str(boost::format("\"%s\", line %d: invalid username") % file % lineno);
 67+ std::cerr << err << '\n';
 68+ return false;
 69+ }
 70+
 71+ uid_t id = uid(username);
 72+ if (id == -1) {
 73+ std::string err = str(boost::format("\"%s\", line %d: user \"%s\" does not exist")
 74+ % file % lineno % username);
 75+ std::cerr << err << '\n';
 76+ return false;
 77+ }
 78+ config.exempt.insert(id);
 79+ } else if (d == "delay") {
 80+ if (!(l >> config.delay)) {
 81+ std::string err = str(boost::format("\"%s\", line %d: invalid delay") % file % lineno);
 82+ std::cerr << err << '\n';
 83+ return false;
 84+ }
 85+ } else if (d == "mailmessage") {
 86+ if (!(l >> config.mailmessage)) {
 87+ std::string err = str(boost::format("\"%s\", line %d: invalid mail message") % file % lineno);
 88+ std::cerr << err << '\n';
 89+ return false;
 90+ }
 91+ } else if (d == "sendmail") {
 92+ if (!(l >> config.sendmail)) {
 93+ std::string err = str(boost::format("\"%s\", line %d: invalid sendmail command") % file % lineno);
 94+ std::cerr << err << '\n';
 95+ return false;
 96+ }
 97+ } else if (d == "pidfile") {
 98+ if (!(l >> config.pidfile)) {
 99+ std::string err = str(boost::format("\"%s\", line %d: invalid pid file") % file % lineno);
 100+ std::cerr << err << '\n';
 101+ return false;
 102+ }
 103+ } else if (d == "minuid") {
 104+ if (!(l >> config.minuid) || config.minuid < 0) {
 105+ std::string err = str(boost::format("\"%s\", line %d: invalid minimum uid") % file % lineno);
 106+ std::cerr << err << '\n';
 107+ return false;
 108+ }
 109+ } else {
 110+ std::string err = str(boost::format("\"%s\", line %d: invalid directive") % file % lineno);
 111+ std::cerr << err << '\n';
 112+ return false;
 113+ }
 114+ }
 115+
 116+ return true;
 117+}
 118+
Property changes on: trunk/tools/slayerd/config.cc
___________________________________________________________________
Added: svn:keywords
1119 + Id Revision
Index: trunk/tools/slayerd/config.h
@@ -0,0 +1,43 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef CONFIG_H
 13+#define CONFIG_H
 14+
 15+#include <cstdlib>
 16+#include <set>
 17+#include <string>
 18+
 19+extern struct config_t {
 20+ std::size_t limit;
 21+ std::size_t thresh;
 22+ int delay;
 23+ std::string mailmessage;
 24+ std::string sendmail;
 25+ std::string pidfile;
 26+ std::set<uid_t> exempt;
 27+ int minuid;
 28+ bool debug;
 29+
 30+ config_t()
 31+ : limit(0)
 32+ , minuid(-1)
 33+ , thresh(0)
 34+ , delay(60)
 35+ , mailmessage(ETCDIR "/mailmessage")
 36+ , sendmail("/usr/lib/sendmail -oi -bm --")
 37+ , pidfile("/var/run/slayerd.pid")
 38+ , debug(false)
 39+ {}
 40+} config;
 41+
 42+bool configure(std::string const &);
 43+
 44+#endif /* !CONFIG_H */
Property changes on: trunk/tools/slayerd/config.h
___________________________________________________________________
Added: svn:keywords
145 + Id Revision
Index: trunk/tools/slayerd/Makefile
@@ -1,4 +1,4 @@
2 -VERSION = 1.6
 2+VERSION = 1.7
33
44 PREFIX ?= /usr/local
55 CONFDIR ?= /etc/slayerd
@@ -6,14 +6,21 @@
77 MANDIR ?= $(PREFIX)/share/man/man8
88
99 CXX = g++
10 -CXXFLAGS = -O2 -g3 -ggdb
 10+CXXFLAGS = -O0 -g3 -ggdb
1111 CPPFLAGS = -DETCDIR=\"$(CONFDIR)\" -DPREFIX=\"$(PREFIX)\" -I/usr/local/include
1212 LDFLAGS = -L/usr/local/lib
13 -SRCS = slayerd.cc process.cc
 13+SRCS = slayerd.cc process.cc config.cc util.cc
1414 OBJS = $(SRCS:.cc=.o)
 15+LIBS =
1516
 17+ifeq ($(shell uname),FreeBSD)
 18+LIBS += -lkvm
 19+endif
 20+
 21+all: slayerd
 22+
1623 slayerd: $(OBJS)
17 - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lboost_filesystem
 24+ $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lboost_filesystem $(LIBS)
1825
1926 install: slayerd
2027 install -d $(DESTDIR)$(SBINDIR)
@@ -28,7 +35,7 @@
2936 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DVERSION=\"$(VERSION)\" -c $<
3037
3138 clean:
32 - rm -f slayerd slayerd.o
 39+ rm -f slayerd *.o
3340
3441 .PHONY: clean install
3542 .SUFFICES: .cc .o

Status & tagging log