add nice and I/O idle config
Stefan Schuermans

Stefan Schuermans commited on 2020-09-19 12:44:39
Showing 16 changed files, with 303 additions and 32 deletions.

... ...
@@ -93,12 +93,18 @@ The configuration file lists directory trees and the ownerships and permissions
93 93
 to set for them. If some of the specified trees are nested within each other,
94 94
 the nested tree(s) is/are excluded from the containing tree(s).
95 95
 
96
-The syntax of the config file is line-based. Each line defines a directory tree
97
-and the ownerships and permissions.
96
+The syntax of the config file is line-based. Each line defines a setting or a
97
+directory tree and the ownerships and permissions for it.
98 98
 
99 99
 Syntax:
100 100
 
101
-* Each line: `tree <user> <group> <permissions> <directory>`
101
+   * process nice-ness (only for `permissionerd`, ignored by `permissonerc`)
102
+     `nice [<niceness value>] [idle]
103
+      * `<niceness value>`: Linux niceness value for daemon process,
104
+                            `19` for nicest (lowest CPU priority)
105
+      * `idle`: set I/O priority class to idle
106
+   * owership and permission configuration:
107
+     `tree <user> <group> <permissions> <directory>`
102 108
       * `<user>`: User name to set as user/owner, `-` to not change the user/owner.
103 109
       * `<group>`: Group name to set as group, `-` to not change the group.
104 110
       * `<permissions>`: Comma-separated list of permission settings.
... ...
@@ -3,12 +3,14 @@ add_library(
3 3
   STATIC
4 4
   include/permissioner/Config.h
5 5
   include/permissioner/Group.h
6
+  include/permissioner/Nice.h
6 7
   include/permissioner/Permissions.h
7 8
   include/permissioner/StringUtils.h
8 9
   include/permissioner/Tree.h
9 10
   include/permissioner/User.h
10 11
   src/Config.cpp
11 12
   src/Group.cpp
13
+  src/Nice.cpp
12 14
   src/Permissions.cpp
13 15
   src/StringUtils.cpp
14 16
   src/Tree.cpp
... ...
@@ -7,6 +7,7 @@
7 7
 #ifndef CONFIG_H
8 8
 #define CONFIG_H
9 9
 
10
+#include <permissioner/Nice.h>
10 11
 #include <permissioner/Tree.h>
11 12
 
12 13
 #include <boost/filesystem.hpp>
... ...
@@ -23,6 +24,9 @@ public:
23 24
    */
24 25
   void parseFile(std::string const &configFileName);
25 26
 
27
+  /// return nice settings
28
+  Nice const & getNice() const;
29
+
26 30
   /// return trees
27 31
   TreeMap const & getTrees() const;
28 32
 
... ...
@@ -32,6 +36,7 @@ public:
32 36
   void setPermissions() const;
33 37
 
34 38
 protected:
39
+  Nice nice;
35 40
   TreeMap trees;
36 41
 };
37 42
 
... ...
@@ -0,0 +1,41 @@
1
+/**
2
+ * Permissioner: set file ownerships and permissions
3
+ * Copyright 2020: Stefan Schuermans, Aachen, Germany <stefan@schuermans.info>
4
+ * Copyleft: GNU GENERAL PUBLIC LICENSE version 3 (see LICENSE)
5
+ */
6
+
7
+#ifndef NICE_H
8
+#define NICE_H
9
+
10
+#include <boost/optional.hpp>
11
+#include <string>
12
+
13
+/// nice (CPU and I/O) priority settings
14
+class Nice {
15
+public:
16
+  Nice();
17
+
18
+  /**
19
+   * @brief parse nice parameters
20
+   * @param[in] parmStr parameter string
21
+   * @throws std::exception if something goes wrong
22
+   */
23
+  void parseParams(std::string const &paramStr);
24
+
25
+  /// return niceness level
26
+  boost::optional<int> getNice() const;
27
+
28
+  /// return whether using I/O idle class
29
+  bool getIoIdle() const;
30
+
31
+  /**
32
+   * @brief set CPU and I/O priority of process
33
+   */
34
+  void apply() const;
35
+
36
+protected:
37
+  boost::optional<int> nice;
38
+  bool ioIdle;
39
+};
40
+
41
+#endif // #ifndef NICE_H
... ...
@@ -21,6 +21,27 @@ public:
21 21
    */
22 22
   static void getNextField(std::string const &str, std::string::size_type &pos,
23 23
                            std::string &field, std::string const &name);
24
+
25
+  /**
26
+   * @brief convert a string to a long integer
27
+   * @param[in] str string on which to operate
28
+   * @param[in] name filed name for exception
29
+   * @return integer value
30
+   * @throws std::exception if something goes wrong
31
+   */
32
+  static long str2long(std::string const &str, std::string const &name);
33
+
34
+  /**
35
+   * @brief convert a string to an integer and check range
36
+   * @param[in] str string on which to operate
37
+   * @param[in] minVal minium value
38
+   * @param[in] maxVal maxium value
39
+   * @param[in] name filed name for exception
40
+   * @return integer value
41
+   * @throws std::exception if something goes wrong
42
+   */
43
+  static int str2intRange(std::string const &str, int minVal, int maxVal,
44
+                          std::string const &name);
24 45
 };
25 46
 
26 47
 #endif // #ifndef STRING_UTILS_H
... ...
@@ -36,6 +36,10 @@ void Config::parseFile(std::string const &configFileName) {
36 36
       continue; // comment line -> ignore
37 37
     }
38 38
     // actual configuration lines
39
+    if (typeStr == "nice") {
40
+      nice.parseParams(line.substr(pos));
41
+      continue;
42
+    }
39 43
     if (typeStr == "tree") {
40 44
       Tree tree;
41 45
       tree.parseParams(line.substr(pos));
... ...
@@ -49,6 +53,10 @@ void Config::parseFile(std::string const &configFileName) {
49 53
   }
50 54
 }
51 55
 
56
+Nice const & Config::getNice() const {
57
+  return nice;
58
+}
59
+
52 60
 TreeMap const & Config::getTrees() const {
53 61
   return trees;
54 62
 }
... ...
@@ -0,0 +1,64 @@
1
+/**
2
+ * Permissioner: set file ownerships and permissions
3
+ * Copyright 2020: Stefan Schuermans, Aachen, Germany <stefan@schuermans.info>
4
+ * Copyleft: GNU GENERAL PUBLIC LICENSE version 3 (see LICENSE)
5
+ */
6
+
7
+#include <permissioner/Nice.h>
8
+
9
+#include <permissioner/StringUtils.h>
10
+
11
+#include <boost/optional.hpp>
12
+#include <string>
13
+#include <sys/time.h>
14
+#include <sys/resource.h>
15
+#include <sys/syscall.h>
16
+
17
+#define IOPRIO_CLASS_SHIFT (13)
18
+#define IOPRIO_PRIO_VALUE(class_, data) (((class_) << IOPRIO_CLASS_SHIFT) | data)
19
+enum {
20
+  IOPRIO_WHO_PROCESS = 1,
21
+  IOPRIO_WHO_PGRP,
22
+  IOPRIO_WHO_USER,
23
+};
24
+enum {
25
+  IOPRIO_CLASS_NONE,
26
+  IOPRIO_CLASS_RT,
27
+  IOPRIO_CLASS_BE,
28
+  IOPRIO_CLASS_IDLE,
29
+};
30
+
31
+Nice::Nice() : ioIdle(false) {}
32
+
33
+void Nice::parseParams(std::string const &paramStr) {
34
+  // format of paramStr is: <nice prm 1> [" " <nice prm 2>]
35
+  // <nice prm> may be <nice value: int> or "idle"
36
+  std::string niceStr;
37
+
38
+  std::string::size_type pos = 0;
39
+  while (pos < paramStr.length()) {
40
+    StringUtils::getNextField(paramStr, pos, niceStr, "nice prm");
41
+    if (niceStr == "idle") {
42
+      ioIdle = true;
43
+      continue;
44
+    }
45
+    nice = StringUtils::str2intRange(niceStr, -20, 19, "nice value");
46
+  }
47
+}
48
+
49
+boost::optional<int> Nice::getNice() const { return nice; }
50
+
51
+bool Nice::getIoIdle() const { return ioIdle; }
52
+
53
+void Nice::apply() const {
54
+  // set CPU priority
55
+  if (nice.is_initialized()) {
56
+    setpriority(PRIO_PROCESS, 0, nice.get());
57
+  }
58
+
59
+  // set idle I/O priority
60
+  if (ioIdle) {
61
+    syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0,
62
+            IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
63
+  }
64
+}
... ...
@@ -6,8 +6,9 @@
6 6
 
7 7
 #include <permissioner/StringUtils.h>
8 8
 
9
-#include <stdexcept>
9
+#include <cstdlib>
10 10
 #include <sstream>
11
+#include <stdexcept>
11 12
 #include <string>
12 13
 
13 14
 void StringUtils::getNextField(std::string const &str,
... ...
@@ -33,3 +34,27 @@ void StringUtils::getNextField(std::string const &str,
33 34
     pos = str.length();
34 35
   }
35 36
 }
37
+
38
+long StringUtils::str2long(std::string const &str, std::string const &name) {
39
+  char const *c_str = str.c_str();
40
+  char *end;
41
+  long val = strtol(c_str, &end, 0);
42
+  if (end == c_str || *end != 0) {
43
+    std::stringstream msg;
44
+    msg << "invalid integer value \"" << str << "\" for <" << name << "> field";
45
+    throw std::runtime_error(msg.str());
46
+  }
47
+  return val;
48
+}
49
+
50
+int StringUtils::str2intRange(std::string const &str, int minVal, int maxVal,
51
+                              std::string const &name) {
52
+  long val = str2long(str, name);
53
+  if (val < minVal || val > maxVal) {
54
+    std::stringstream msg;
55
+    msg << "value " << val << "of <" << name << "> field out of range "
56
+        << minVal << " - " << maxVal;
57
+    throw std::runtime_error(msg.str());
58
+  }
59
+  return val;
60
+}
... ...
@@ -45,6 +45,9 @@ int main(int argc, char const **argv) {
45 45
   std::cout << "permissionerd (" << configFileName << ") starting"
46 46
             << std::endl;
47 47
 
48
+  // set nicecess of process
49
+  config.getNice().apply();
50
+
48 51
   // continuously set ownership and permissions
49 52
   int ret = EXIT_SUCCESS;
50 53
   while (go_on) {
... ...
@@ -13,7 +13,7 @@ add_test(
13 13
   NAME
14 14
   testConfig
15 15
   COMMAND
16
-  ${CMAKE_CURRENT_BINARY_DIR}/testConfig test.cfg
16
+  ${CMAKE_CURRENT_BINARY_DIR}/testConfig
17 17
   WORKING_DIRECTORY
18 18
   ${CMAKE_CURRENT_SOURCE_DIR}
19 19
 )
... ...
@@ -0,0 +1 @@
1
+# comment
... ...
@@ -0,0 +1 @@
1
+nice idle
... ...
@@ -0,0 +1 @@
1
+nice 19
... ...
@@ -0,0 +1 @@
1
+nice 19 idle
... ...
@@ -7,8 +7,8 @@
7 7
 #include <permissioner/Config.h>
8 8
 #include <permissioner/Group.h>
9 9
 #include <permissioner/Permissions.h>
10
-#include <permissioner/User.h>
11 10
 #include <permissioner/Tree.h>
11
+#include <permissioner/User.h>
12 12
 
13 13
 #include <boost/filesystem.hpp>
14 14
 #include <boost/optional.hpp>
... ...
@@ -18,11 +18,95 @@
18 18
 #include <iostream>
19 19
 #include <string>
20 20
 
21
-bool check(TreeMap const &treeMap, std::string const &rel_path,
21
+int testEmpty() {
22
+  Config config;
23
+  config.parseFile("empty.cfg");
24
+
25
+  int ret = EXIT_SUCCESS;
26
+
27
+  Nice const &nice = config.getNice();
28
+  if (nice.getNice().is_initialized()) {
29
+    std::cerr << "unexpected nice value: " << nice.getNice().get() << std::endl;
30
+    ret = EXIT_FAILURE;
31
+  }
32
+  if (nice.getIoIdle()) {
33
+    std::cerr << "unexpected I/O idle" << std::endl;
34
+    ret = EXIT_FAILURE;
35
+  }
36
+
37
+  TreeMap const &treeMap = config.getTrees();
38
+  if (treeMap.size() != 0) {
39
+    std::cerr << "unexpected trees: " << treeMap.size() << std::endl;
40
+    ret = EXIT_FAILURE;
41
+  }
42
+
43
+  return ret;
44
+}
45
+
46
+int testNice() {
47
+  int ret = EXIT_SUCCESS;
48
+
49
+  {
50
+    Config config;
51
+    config.parseFile("nice.cfg");
52
+
53
+    Nice const &nice = config.getNice();
54
+    if (!nice.getNice().is_initialized()) {
55
+      std::cerr << "expected nice value, but got none" << std::endl;
56
+      ret = EXIT_FAILURE;
57
+    } else {
58
+      if (nice.getNice().get() != 19) {
59
+        std::cerr << "unexpected nice value: " << nice.getNice().get()
60
+                  << ", expected 19" << std::endl;
61
+        ret = EXIT_FAILURE;
62
+      }
63
+    }
64
+    if (nice.getIoIdle()) {
65
+      std::cerr << "unexpected I/O idle" << std::endl;
66
+      ret = EXIT_FAILURE;
67
+    }
68
+  }
69
+
70
+  {
71
+    Config config;
72
+    config.parseFile("idle.cfg");
73
+
74
+    Nice const &nice = config.getNice();
75
+    if (nice.getNice().is_initialized()) {
76
+      std::cerr << "unexpected nice value: " << nice.getNice().get()
77
+                << std::endl;
78
+      ret = EXIT_FAILURE;
79
+    }
80
+    if (!nice.getIoIdle()) {
81
+      std::cerr << "expected I/O idle, but did not get it" << std::endl;
82
+      ret = EXIT_FAILURE;
83
+    }
84
+  }
85
+
86
+  {
87
+    Config config;
88
+    config.parseFile("nice_idle.cfg");
89
+
90
+    Nice const &nice = config.getNice();
91
+    if (!nice.getNice().is_initialized()) {
92
+      std::cerr << "expected nice value, but got none" << std::endl;
93
+      ret = EXIT_FAILURE;
94
+    } else {
95
+      if (nice.getNice().get() != 19) {
96
+        std::cerr << "unexpected nice value: " << nice.getNice().get()
97
+                  << ", expected 19" << std::endl;
98
+        ret = EXIT_FAILURE;
99
+      }
100
+    }
101
+  }
102
+
103
+  return ret;
104
+}
105
+
106
+bool checkTrees(TreeMap const &treeMap, std::string const &rel_path,
22 107
                 boost::optional<std::string> user,
23
-           boost::optional<std::string> group,
24
-           mode_t setMode, mode_t setCondMode,
25
-           mode_t clearMode, mode_t clearCondMode) {
108
+                boost::optional<std::string> group, mode_t setMode,
109
+                mode_t setCondMode, mode_t clearMode, mode_t clearCondMode) {
26 110
   std::string path = boost::filesystem::canonical(rel_path).string();
27 111
   TreeMap::const_iterator itTree = treeMap.find(path);
28 112
   if (itTree == treeMap.end()) {
... ...
@@ -50,41 +134,37 @@ bool check(TreeMap const &treeMap, std::string const &rel_path,
50 134
   }
51 135
   Permissions const &perms = tree.getPermissions();
52 136
   if (perms.getSet() != setMode) {
53
-    std::cerr << "tree map entry \"" << path
54
-              << "\": unexpected set mode "
55
-              << std::oct << perms.getSet() << " != " << setMode
56
-              << std::dec << std::endl;
137
+    std::cerr << "tree map entry \"" << path << "\": unexpected set mode "
138
+              << std::oct << perms.getSet() << " != " << setMode << std::dec
139
+              << std::endl;
57 140
     ret = false;
58 141
   }
59 142
   if (perms.getSetCond() != setCondMode) {
60
-    std::cerr << "tree map entry \"" << path
61
-              << "\": unexpected set cond mode "
143
+    std::cerr << "tree map entry \"" << path << "\": unexpected set cond mode "
62 144
               << std::oct << perms.getSetCond() << " != " << setCondMode
63 145
               << std::dec << std::endl;
64 146
     ret = false;
65 147
   }
66 148
   if (perms.getClear() != clearMode) {
67
-    std::cerr << "tree map entry \"" << path
68
-              << "\": unexpected clear mode "
69
-              << std::oct << perms.getClear() << " != " << clearMode
70
-              << std::dec << std::endl;
149
+    std::cerr << "tree map entry \"" << path << "\": unexpected clear mode "
150
+              << std::oct << perms.getClear() << " != " << clearMode << std::dec
151
+              << std::endl;
71 152
     ret = false;
72 153
   }
73 154
   if (perms.getClearCond() != clearCondMode) {
74 155
     std::cerr << "tree map entry \"" << path
75
-              << "\": unexpected clear cond mode "
76
-              << std::oct << perms.getClearCond() << " != " << clearCondMode
77
-              << std::dec << std::endl;
156
+              << "\": unexpected clear cond mode " << std::oct
157
+              << perms.getClearCond() << " != " << clearCondMode << std::dec
158
+              << std::endl;
78 159
     ret = false;
79 160
   }
80 161
 
81 162
   return ret;
82 163
 }
83 164
 
84
-int main(int argc, char const **argv) {
85
-  (void)argc;
165
+int testTrees() {
86 166
   Config config;
87
-  config.parseFile(argv[1]);
167
+  config.parseFile("trees.cfg");
88 168
 
89 169
   int ret = EXIT_SUCCESS;
90 170
 
... ...
@@ -93,16 +173,28 @@ int main(int argc, char const **argv) {
93 173
     std::cerr << "unexpected number of trees: " << treeMap.size() << std::endl;
94 174
     ret = EXIT_FAILURE;
95 175
   }
96
-  if (! check(treeMap, "some/dir", std::string("nobody"),
97
-                                   std::string("nogroup"),
98
-                                   0011, 0044, 0022, 000)) {
176
+  if (!checkTrees(treeMap, "some/dir", std::string("nobody"),
177
+                  std::string("nogroup"), 0011, 0044, 0022, 000)) {
99 178
     ret = EXIT_FAILURE;
100 179
   }
101
-  if (! check(treeMap, "some/other/dir", boost::none,
102
-                                         boost::none,
103
-                                         0777, 0000, 0000, 0000)) {
180
+  if (!checkTrees(treeMap, "some/other/dir", boost::none, boost::none, 0777,
181
+                  0000, 0000, 0000)) {
104 182
     ret = EXIT_FAILURE;
105 183
   }
106 184
 
107 185
   return ret;
108 186
 }
187
+
188
+void merge_ret(int &ret, int ret2) {
189
+  if (ret == EXIT_SUCCESS && ret2 != EXIT_SUCCESS) {
190
+    ret = ret2;
191
+  }
192
+}
193
+
194
+int main() {
195
+  int ret = EXIT_SUCCESS;
196
+  merge_ret(ret, testEmpty());
197
+  merge_ret(ret, testNice());
198
+  merge_ret(ret, testTrees());
199
+  return ret;
200
+}