Apache Mesos
sysctl.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_SYSCTL_HPP__
14 #define __STOUT_OS_SYSCTL_HPP__
15 
16 // Only provide sysctl support for OS X and FreeBSD.
17 #if !defined(__APPLE__) && !defined(__FreeBSD__)
18 #error "stout/os/sysctl.hpp is only available on OS X and FreeBSD."
19 #endif
20 
21 #include <sys/sysctl.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 
25 #include <string>
26 #include <vector>
27 
28 #include <stout/error.hpp>
29 #include <stout/none.hpp>
30 #include <stout/option.hpp>
31 #include <stout/strings.hpp>
32 #include <stout/try.hpp>
33 
34 namespace os {
35 
36 // Provides an abstraction for getting system information via the
37 // underlying 'sysctl' system call. You describe the sysctl
38 // "Management Information Base" (MIB) name via the constructor, for
39 // example, to describe "maximum number of processes allowed in the
40 // system" you would do:
41 //
42 // os::sysctl(CTL_KERN, KERN_MAXPROC)
43 //
44 // To _retrieve_ the value you need to use one of the 'integer',
45 // 'string', 'table', or 'time' methods to indicate the type of the
46 // value being retrieved. For example:
47 //
48 // Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
49 //
50 // Note that the 'table' method requires specifying a length. If you
51 // would like the length to be looked up dynamically you can just pass
52 // None. Here's an example using 'table' that builds on above:
53 //
54 // Try<vector<kinfo_proc>> processes =
55 // os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxprox.get());
56 //
57 // TODO(benh): Provide an 'integer(i)', 'string(s)', and 'table(t)' to
58 // enable setting system information.
59 struct sysctl
60 {
61  // Note that we create a constructor for each number of levels
62  // because we can't pick a suitable default for unused levels (in
63  // order to distinguish no value from some value) and while Option
64  // would solve that it could also cause people to use None which
65  // we'd need to later handle as an error.
66  explicit sysctl(int level1);
67  sysctl(int level1, int level2);
68  sysctl(int level1, int level2, int level3);
69  sysctl(int level1, int level2, int level3, int level4);
70  sysctl(int level1, int level2, int level3, int level4, int level5);
71  ~sysctl();
72 
73  // Get system information as an integer.
74 private: struct Integer; // Forward declaration.
75 public:
76  Integer integer() const;
77 
78  // Get system information as a string.
79  Try<std::string> string() const;
80 
81  // Get system information as a timeval.
82  Try<timeval> time() const;
83 
84  // Get system information as a table, optionally specifying a
85  // length. Note that this function is lazy and will not actually
86  // perform the syscall until you cast (implicitly or explicitly) a
87  // 'Table' to a std::vector<T>. For example, to get the first 10
88  // processes in the process table you can do:
89  //
90  // Try<std::vector<kinfo_proc>> processes =
91  // os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(10);
92  //
93 private: struct Table; // Forward declaration.
94 public:
95  Table table(const Option<size_t>& length = None()) const;
96 
97 private:
98  struct Integer
99  {
100  Integer(int _levels, int* _name);
101 
102  template <typename T>
103  operator Try<T>();
104 
105  const int levels;
106  int* name;
107  };
108 
109  struct Table
110  {
111  Table(int _levels, int* _name, const Option<size_t>& _length);
112 
113  template <typename T>
114  operator Try<std::vector<T>>();
115 
116  const int levels;
117  int* name;
118  Option<size_t> length;
119  };
120 
121  const int levels;
122  int* name;
123 };
124 
125 
126 inline sysctl::sysctl(int level1)
127  : levels(1), name(new int[levels])
128 {
129  name[0] = level1;
130 }
131 
132 
133 inline sysctl::sysctl(int level1, int level2)
134  : levels(2), name(new int[levels])
135 {
136  name[0] = level1;
137  name[1] = level2;
138 }
139 
140 
141 inline sysctl::sysctl(int level1, int level2, int level3)
142  : levels(3), name(new int[levels])
143 {
144  name[0] = level1;
145  name[1] = level2;
146  name[2] = level3;
147 }
148 
149 
150 inline sysctl::sysctl(int level1, int level2, int level3, int level4)
151  : levels(4), name(new int[levels])
152 {
153  name[0] = level1;
154  name[1] = level2;
155  name[2] = level3;
156  name[3] = level4;
157 }
158 
159 
161  int level1,
162  int level2,
163  int level3,
164  int level4,
165  int level5)
166  : levels(5), name(new int[levels])
167 {
168  name[0] = level1;
169  name[1] = level2;
170  name[2] = level3;
171  name[3] = level4;
172  name[4] = level5;
173 }
174 
175 
177 {
178  delete[] name;
179 }
180 
181 
182 inline sysctl::Integer sysctl::integer() const
183 {
184  return Integer(levels, name);
185 }
186 
187 
189 {
190  // First determine the size of the string.
191  size_t size = 0;
192  if (::sysctl(name, levels, nullptr, &size, nullptr, 0) == -1) {
193  return ErrnoError();
194  }
195 
196  // Now read it.
197  size_t length = size / sizeof(char);
198  char* temp = new char[length];
199  if (::sysctl(name, levels, temp, &size, nullptr, 0) == -1) {
200  Error error = ErrnoError();
201  delete[] temp;
202  return error;
203  }
204 
205  // TODO(benh): It's possible that the value has changed since we
206  // determined it's length above. We should really check that we
207  // get back the same length and if not throw an error.
208 
209  // The "string" in 'temp' might include null bytes, so to get all of
210  // the data we need to create a string with 'size' (but we exclude
211  // the last null byte via 'size - 1').
212  std::string result(temp, size - 1);
213  delete[] temp;
214  return result;
215 }
216 
217 
219 {
220  timeval result;
221  size_t size = sizeof(result);
222  if (::sysctl(name, levels, &result, &size, nullptr, 0) == -1) {
223  return ErrnoError();
224  }
225  return result;
226 }
227 
228 
229 inline sysctl::Table sysctl::table(const Option<size_t>& length) const
230 {
231  return Table(levels, name, length);
232 }
233 
234 
235 inline sysctl::Integer::Integer(
236  int _levels,
237  int* _name)
238  : levels(_levels),
239  name(_name)
240 {}
241 
242 
243 template <typename T>
244 sysctl::Integer::operator Try<T>()
245 {
246  T i;
247  size_t size = sizeof(i);
248  if (::sysctl(name, levels, &i, &size, nullptr, 0) == -1) {
249  return ErrnoError();
250  }
251  return i;
252 }
253 
254 
255 inline sysctl::Table::Table(
256  int _levels,
257  int* _name,
258  const Option<size_t>& _length)
259  : levels(_levels),
260  name(_name),
261  length(_length)
262 {}
263 
264 
265 template <typename T>
266 sysctl::Table::operator Try<std::vector<T>>()
267 {
268  size_t size = 0;
269  if (length.isNone()) {
270  if (::sysctl(name, levels, nullptr, &size, nullptr, 0) == -1) {
271  return ErrnoError();
272  }
273  if (size % sizeof(T) != 0) {
274  return Error("Failed to determine the length of result, "
275  "amount of available data is not a multiple "
276  "of the table type");
277  }
278  length = Option<size_t>(size / sizeof(T));
279  }
280 
281  T* ts = new T[length.get()];
282  size = length.get() * sizeof(T);
283  if (::sysctl(name, levels, ts, &size, nullptr, 0) == -1) {
284  Error error = ErrnoError();
285  delete[] ts;
286  return error;
287  }
288 
289  // TODO(benh): It's possible that the value has changed since we
290  // determined it's length above (or from what was specified). We
291  // should really check that we get back the same length and if not
292  // throw an error.
293 
294  length = size / sizeof(T);
295 
296  std::vector<T> results;
297  for (size_t i = 0; i < length.get(); i++) {
298  results.push_back(ts[i]);
299  }
300  delete[] ts;
301  return results;
302 }
303 
304 } // namespace os {
305 
306 #endif // __STOUT_OS_SYSCTL_HPP__
Definition: errorbase.hpp:36
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:130
Definition: check.hpp:33
Definition: errorbase.hpp:50
Definition: posix_signalhandler.hpp:23
Try< std::string > string() const
Definition: sysctl.hpp:188
Table table(const Option< size_t > &length=None()) const
Definition: sysctl.hpp:229
sysctl(int level1)
Definition: sysctl.hpp:126
~sysctl()
Definition: sysctl.hpp:176
Integer integer() const
Definition: sysctl.hpp:182
Definition: none.hpp:27
std::string error(const std::string &msg, uint32_t code)
Definition: sysctl.hpp:59
std::string temp()
Definition: temp.hpp:27
Try< timeval > time() const
Definition: sysctl.hpp:218