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 |
1 | 84 | + 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 |
1 | 155 | + Id Revision |
Index: trunk/tools/slayerd/process.cc |
— | — | @@ -6,8 +6,10 @@ |
7 | 7 | * warranty. |
8 | 8 | */ |
9 | 9 | |
10 | | -#ifdef __linux__ |
| 10 | +#if defined(__linux__) |
11 | 11 | # include "proc_linux.cc" |
| 12 | +#elif defined(__FreeBSD__) |
| 13 | +# include "proc_freebsd.cc" |
12 | 14 | #else |
13 | 15 | # error dont know how to enumerate processes on this platform |
14 | 16 | #endif |
Property changes on: trunk/tools/slayerd/process.cc |
___________________________________________________________________ |
Added: svn:keywords |
15 | 17 | + 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 |
1 | 26 | + Id Revision |
Index: trunk/tools/slayerd/process.h |
— | — | @@ -11,7 +11,9 @@ |
12 | 12 | #ifndef PROCESS_H |
13 | 13 | #define PROCESS_H |
14 | 14 | |
| 15 | +#include <sys/types.h> |
15 | 16 | #include <vector> |
| 17 | +#include <boost/shared_ptr.hpp> |
16 | 18 | |
17 | 19 | struct process { |
18 | 20 | typedef boost::shared_ptr<process> pointer; |
Property changes on: trunk/tools/slayerd/process.h |
___________________________________________________________________ |
Added: svn:keywords |
19 | 21 | + Id Revision |
Index: trunk/tools/slayerd/proc_linux.cc |
— | — | @@ -71,6 +71,7 @@ |
72 | 72 | unsigned long _rt_priority, _policy; |
73 | 73 | int _mshare, _mtext, _mlib, _mdata; |
74 | 74 | pid_t _ppid, _pgrp, _sid, _tty, _tpgid, _state, _msize, _nice; |
| 75 | + int pagesz = sysconf(_SC_PAGE_SIZE); |
75 | 76 | |
76 | 77 | { |
77 | 78 | std::ifstream f((pth / "stat").native_file_string().c_str()); |
— | — | @@ -109,6 +110,14 @@ |
110 | 111 | if (!std::getline(f, _fullcomm)) |
111 | 112 | throw std::runtime_error("could not parse cmdline"); |
112 | 113 | } |
| 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', ' '); |
113 | 122 | } |
114 | 123 | |
115 | 124 | template<typename C> |
Property changes on: trunk/tools/slayerd/proc_linux.cc |
___________________________________________________________________ |
Added: svn:keywords |
116 | 125 | + Id Revision |
Index: trunk/tools/slayerd/slayerd.cc |
— | — | @@ -12,81 +12,27 @@ |
13 | 13 | * slayerd: monitor user activity and regulate users using too much RAM. |
14 | 14 | */ |
15 | 15 | |
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> |
30 | 16 | #include <sys/wait.h> |
31 | | -#include <unistd.h> |
32 | | -#include <pwd.h> |
33 | | -#include <signal.h> |
| 17 | +#include <sys/mman.h> |
34 | 18 | #include <syslog.h> |
35 | 19 | |
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> |
39 | 26 | #include <boost/format.hpp> |
40 | | -#include <boost/shared_ptr.hpp> |
| 27 | +#include <boost/lexical_cast.hpp> |
41 | 28 | |
42 | 29 | #include "process.h" |
| 30 | +#include "config.h" |
| 31 | +#include "util.h" |
43 | 32 | |
44 | | -namespace fs = boost::filesystem; |
45 | | - |
46 | 33 | namespace { |
47 | 34 | std::string CONFFILE = "/etc/slayerd/slayerd.conf"; |
48 | 35 | } |
49 | 36 | |
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 | | - |
91 | 37 | struct user { |
92 | 38 | user() : uid(-1), rss(0) {} |
93 | 39 | |
— | — | @@ -95,23 +41,9 @@ |
96 | 42 | std::vector<process::pointer> processes; |
97 | 43 | }; |
98 | 44 | |
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); |
108 | 47 | |
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 | | - |
116 | 48 | void |
117 | 49 | version(void) { |
118 | 50 | std::cerr << "slayerd " << VERSION << "\n"; |
— | — | @@ -125,222 +57,6 @@ |
126 | 58 | } |
127 | 59 | |
128 | 60 | 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 |
345 | 61 | rmpidfile(void) |
346 | 62 | { |
347 | 63 | unlink(config.pidfile.c_str()); |
— | — | @@ -384,14 +100,11 @@ |
385 | 101 | int |
386 | 102 | main(int argc, char **argv) |
387 | 103 | { |
388 | | - int pagesize = sysconf(_SC_PAGE_SIZE), fflag = 0; |
| 104 | + int fflag = 0; |
389 | 105 | int c; |
390 | 106 | |
391 | 107 | config.exempt.insert(0); /* root is always exempt */ |
392 | 108 | |
393 | | - char nodename[255]; |
394 | | - gethostname(nodename, sizeof nodename); |
395 | | - |
396 | 109 | while ((c = getopt(argc, argv, "fvhc:D")) != -1) { |
397 | 110 | switch (c) { |
398 | 111 | case 'f': |
— | — | @@ -428,7 +141,7 @@ |
429 | 142 | if (!config.debug) |
430 | 143 | openlog("slayerd", LOG_PID, LOG_DAEMON); |
431 | 144 | |
432 | | - if (!configure()) |
| 145 | + if (!configure(CONFFILE)) |
433 | 146 | return 1; |
434 | 147 | |
435 | 148 | if (config.limit == 0 || config.thresh == 0) { |
— | — | @@ -451,106 +164,115 @@ |
452 | 165 | % config.delay % (config.limit / 1024 / 1024) % (config.thresh / 1024 / 1024))); |
453 | 166 | |
454 | 167 | for (;;) { |
455 | | - std::vector<process::pointer> processes(enumerate_processes()); |
| 168 | + run_once(); |
| 169 | + sleep(config.delay); |
| 170 | + } |
| 171 | +} |
456 | 172 | |
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 | +}; |
464 | 178 | |
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()); |
470 | 183 | |
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; |
476 | 196 | } |
477 | 197 | |
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]; |
480 | 203 | } |
481 | 204 | |
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 | + } |
486 | 208 | |
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 | +} |
490 | 213 | |
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; |
493 | 218 | |
494 | | - if (config.minuid >= 0 && u.uid < config.minuid) |
495 | | - continue; |
| 219 | + if (config.exempt.find(u.uid) != config.exempt.end()) |
| 220 | + return; |
496 | 221 | |
497 | | - if (bytes < config.limit) |
498 | | - continue; |
| 222 | + if (config.minuid >= 0 && u.uid < config.minuid) |
| 223 | + return; |
499 | 224 | |
500 | | - std::string uname = username(u.uid); |
501 | | - std::string process_list; |
| 225 | + if (bytes < config.limit) |
| 226 | + return; |
502 | 227 | |
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; |
507 | 230 | |
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))); |
509 | 235 | |
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()); |
512 | 237 | |
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]; |
516 | 240 | |
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(); |
520 | 243 | |
521 | | - if (!config.debug) |
522 | | - kill(p->pid(), SIGKILL); |
| 244 | + if (!config.debug) |
| 245 | + kill(p->pid(), SIGKILL); |
523 | 246 | |
524 | | - std::size_t thissize = std::size_t(p->rss()) * pagesize; |
| 247 | + std::size_t thissize = std::size_t(p->rss()); |
525 | 248 | |
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))); |
530 | 253 | |
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()); |
534 | 257 | |
535 | | - bytes -= thissize; |
536 | | - u.processes.erase(u.processes.begin()); |
537 | | - } |
| 258 | + bytes -= thissize; |
| 259 | + u.processes.erase(u.processes.begin()); |
| 260 | + } |
538 | 261 | |
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))); |
541 | 264 | |
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); |
549 | 267 | |
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; |
554 | 275 | |
555 | | - sleep(config.delay); |
556 | | - } |
| 276 | + std::string message = replace_file(config.mailmessage, msgvars); |
| 277 | + if (!config.debug && !message.empty()) |
| 278 | + sendmail(uname, message); |
557 | 279 | } |
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 |
1 | 119 | + 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 |
1 | 45 | + Id Revision |
Index: trunk/tools/slayerd/Makefile |
— | — | @@ -1,4 +1,4 @@ |
2 | | -VERSION = 1.6 |
| 2 | +VERSION = 1.7 |
3 | 3 | |
4 | 4 | PREFIX ?= /usr/local |
5 | 5 | CONFDIR ?= /etc/slayerd |
— | — | @@ -6,14 +6,21 @@ |
7 | 7 | MANDIR ?= $(PREFIX)/share/man/man8 |
8 | 8 | |
9 | 9 | CXX = g++ |
10 | | -CXXFLAGS = -O2 -g3 -ggdb |
| 10 | +CXXFLAGS = -O0 -g3 -ggdb |
11 | 11 | CPPFLAGS = -DETCDIR=\"$(CONFDIR)\" -DPREFIX=\"$(PREFIX)\" -I/usr/local/include |
12 | 12 | LDFLAGS = -L/usr/local/lib |
13 | | -SRCS = slayerd.cc process.cc |
| 13 | +SRCS = slayerd.cc process.cc config.cc util.cc |
14 | 14 | OBJS = $(SRCS:.cc=.o) |
| 15 | +LIBS = |
15 | 16 | |
| 17 | +ifeq ($(shell uname),FreeBSD) |
| 18 | +LIBS += -lkvm |
| 19 | +endif |
| 20 | + |
| 21 | +all: slayerd |
| 22 | + |
16 | 23 | slayerd: $(OBJS) |
17 | | - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lboost_filesystem |
| 24 | + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lboost_filesystem $(LIBS) |
18 | 25 | |
19 | 26 | install: slayerd |
20 | 27 | install -d $(DESTDIR)$(SBINDIR) |
— | — | @@ -28,7 +35,7 @@ |
29 | 36 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DVERSION=\"$(VERSION)\" -c $< |
30 | 37 | |
31 | 38 | clean: |
32 | | - rm -f slayerd slayerd.o |
| 39 | + rm -f slayerd *.o |
33 | 40 | |
34 | 41 | .PHONY: clean install |
35 | 42 | .SUFFICES: .cc .o |