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 ¶mStr); |
|
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 ¶mStr) { |
|
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) { |
... | ... |
@@ -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 |
+} |