Stefan Schuermans commited on 2020-09-19 15:03:32
Showing 11 changed files, with 248 additions and 18 deletions.
| ... | ... |
@@ -103,6 +103,14 @@ Syntax: |
| 103 | 103 |
* `<niceness value>`: Linux niceness value for daemon process, |
| 104 | 104 |
`19` for nicest (lowest CPU priority) |
| 105 | 105 |
* `idle`: set I/O priority class to idle |
| 106 |
+ * sleep time after each file: `sleepTime <seconds>` |
|
| 107 |
+ * range 0 - 1 s, default 1e-6 s |
|
| 108 |
+ * wait time factor: `waitFactor <floating-point factor>` |
|
| 109 |
+ * how long to wait after each tree traversal relative to the duration of |
|
| 110 |
+ the traversal |
|
| 111 |
+ * range 0 - 1000, default 10 |
|
| 112 |
+ * additional wait time after each tree traversal: `waitTime <seconds>` |
|
| 113 |
+ * range 0 - 3600 s, default 1 s |
|
| 106 | 114 |
* owership and permission configuration: |
| 107 | 115 |
`tree <user> <group> <permissions> <directory>` |
| 108 | 116 |
* `<user>`: User name to set as user/owner, `-` to not change the user/owner. |
| ... | ... |
@@ -3,6 +3,7 @@ add_library( |
| 3 | 3 |
STATIC |
| 4 | 4 |
include/permissioner/Callback.h |
| 5 | 5 |
include/permissioner/Config.h |
| 6 |
+ include/permissioner/Float.h |
|
| 6 | 7 |
include/permissioner/Group.h |
| 7 | 8 |
include/permissioner/Nice.h |
| 8 | 9 |
include/permissioner/Permissions.h |
| ... | ... |
@@ -11,6 +12,7 @@ add_library( |
| 11 | 12 |
include/permissioner/User.h |
| 12 | 13 |
src/Callback.cpp |
| 13 | 14 |
src/Config.cpp |
| 15 |
+ src/Float.cpp |
|
| 14 | 16 |
src/Group.cpp |
| 15 | 17 |
src/Nice.cpp |
| 16 | 18 |
src/Permissions.cpp |
| ... | ... |
@@ -8,6 +8,7 @@ |
| 8 | 8 |
#define CONFIG_H |
| 9 | 9 |
|
| 10 | 10 |
#include <permissioner/Callback.h> |
| 11 |
+#include <permissioner/Float.h> |
|
| 11 | 12 |
#include <permissioner/Nice.h> |
| 12 | 13 |
#include <permissioner/Tree.h> |
| 13 | 14 |
|
| ... | ... |
@@ -18,6 +19,8 @@ |
| 18 | 19 |
/// configuration file |
| 19 | 20 |
class Config {
|
| 20 | 21 |
public: |
| 22 |
+ Config(); |
|
| 23 |
+ |
|
| 21 | 24 |
/** |
| 22 | 25 |
* @brief parse configuration file |
| 23 | 26 |
* @param[in] configFileName name of configuation file |
| ... | ... |
@@ -28,6 +31,15 @@ public: |
| 28 | 31 |
/// return nice settings |
| 29 | 32 |
Nice const & getNice() const; |
| 30 | 33 |
|
| 34 |
+ /// return sleep time setting |
|
| 35 |
+ Float const & getSleepTime() const; |
|
| 36 |
+ |
|
| 37 |
+ /// return wait factor setting |
|
| 38 |
+ Float const & getWaitFactor() const; |
|
| 39 |
+ |
|
| 40 |
+ /// return wait time setting |
|
| 41 |
+ Float const & getWaitTime() const; |
|
| 42 |
+ |
|
| 31 | 43 |
/// return trees |
| 32 | 44 |
TreeMap const & getTrees() const; |
| 33 | 45 |
|
| ... | ... |
@@ -40,6 +52,9 @@ public: |
| 40 | 52 |
|
| 41 | 53 |
protected: |
| 42 | 54 |
Nice nice; |
| 55 |
+ Float sleepTime; |
|
| 56 |
+ Float waitFactor; |
|
| 57 |
+ Float waitTime; |
|
| 43 | 58 |
TreeMap trees; |
| 44 | 59 |
}; |
| 45 | 60 |
|
| ... | ... |
@@ -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 FLOAT_H |
|
| 8 |
+#define FLOAT_H |
|
| 9 |
+ |
|
| 10 |
+#include <string> |
|
| 11 |
+ |
|
| 12 |
+/// a floating point number in the settings |
|
| 13 |
+class Float {
|
|
| 14 |
+public: |
|
| 15 |
+ /** |
|
| 16 |
+ * @brief initialize floating-point setting |
|
| 17 |
+ * @param[in] name name of value for error messages |
|
| 18 |
+ * @param[in] defVal default value |
|
| 19 |
+ * @param[in] minVal minimum value |
|
| 20 |
+ * @param[in] maxVal maximum value |
|
| 21 |
+ */ |
|
| 22 |
+ Float(std::string const &name, float minVal, float maxVal, float defVal); |
|
| 23 |
+ |
|
| 24 |
+ /** |
|
| 25 |
+ * @brief parse floating-point value parameter |
|
| 26 |
+ * @param[in] parmStr parameter string |
|
| 27 |
+ * @throws std::exception if something goes wrong |
|
| 28 |
+ */ |
|
| 29 |
+ void parseParams(std::string const ¶mStr); |
|
| 30 |
+ |
|
| 31 |
+ /// return floating-point value |
|
| 32 |
+ float get() const; |
|
| 33 |
+ |
|
| 34 |
+protected: |
|
| 35 |
+ std::string name; ///< name for error messages |
|
| 36 |
+ float minVal; ///< minimum value |
|
| 37 |
+ float maxVal; ///< maximum value |
|
| 38 |
+ float val; ///< configured value |
|
| 39 |
+}; |
|
| 40 |
+ |
|
| 41 |
+#endif // #ifndef FLOAT_H |
| ... | ... |
@@ -42,6 +42,27 @@ public: |
| 42 | 42 |
*/ |
| 43 | 43 |
static int str2intRange(std::string const &str, int minVal, int maxVal, |
| 44 | 44 |
std::string const &name); |
| 45 |
+ |
|
| 46 |
+ /** |
|
| 47 |
+ * @brief convert a string to a floating-point value |
|
| 48 |
+ * @param[in] str string on which to operate |
|
| 49 |
+ * @param[in] name filed name for exception |
|
| 50 |
+ * @return floating-point value |
|
| 51 |
+ * @throws std::exception if something goes wrong |
|
| 52 |
+ */ |
|
| 53 |
+ static float str2float(std::string const &str, std::string const &name); |
|
| 54 |
+ |
|
| 55 |
+ /** |
|
| 56 |
+ * @brief convert a string to a floating-point value and check range |
|
| 57 |
+ * @param[in] str string on which to operate |
|
| 58 |
+ * @param[in] minVal minium value |
|
| 59 |
+ * @param[in] maxVal maxium value |
|
| 60 |
+ * @param[in] name filed name for exception |
|
| 61 |
+ * @return floating-point value |
|
| 62 |
+ * @throws std::exception if something goes wrong |
|
| 63 |
+ */ |
|
| 64 |
+ static float str2floatRange(std::string const &str, float minVal, |
|
| 65 |
+ float maxVal, std::string const &name); |
|
| 45 | 66 |
}; |
| 46 | 67 |
|
| 47 | 68 |
#endif // #ifndef STRING_UTILS_H |
| ... | ... |
@@ -7,6 +7,7 @@ |
| 7 | 7 |
#include <permissioner/Config.h> |
| 8 | 8 |
|
| 9 | 9 |
#include <permissioner/Callback.h> |
| 10 |
+#include <permissioner/Float.h> |
|
| 10 | 11 |
#include <permissioner/StringUtils.h> |
| 11 | 12 |
#include <permissioner/Tree.h> |
| 12 | 13 |
|
| ... | ... |
@@ -17,6 +18,11 @@ |
| 17 | 18 |
#include <stdexcept> |
| 18 | 19 |
#include <string> |
| 19 | 20 |
|
| 21 |
+Config::Config() |
|
| 22 |
+ : sleepTime("sleepTime", 0.0f, 1.0f, 1.0e-6f),
|
|
| 23 |
+ waitFactor("waitFactor", 0.0f, 1.0e3f, 10.0f),
|
|
| 24 |
+ waitTime("waitTime", 0.0f, 3.6e3f, 1.0f) {}
|
|
| 25 |
+ |
|
| 20 | 26 |
void Config::parseFile(std::string const &configFileName) {
|
| 21 | 27 |
std::ifstream configFile(configFileName, std::ios::in); |
| 22 | 28 |
if (!configFile.is_open()) {
|
| ... | ... |
@@ -41,6 +47,18 @@ void Config::parseFile(std::string const &configFileName) {
|
| 41 | 47 |
nice.parseParams(line.substr(pos)); |
| 42 | 48 |
continue; |
| 43 | 49 |
} |
| 50 |
+ if (typeStr == "sleepTime") {
|
|
| 51 |
+ sleepTime.parseParams(line.substr(pos)); |
|
| 52 |
+ continue; |
|
| 53 |
+ } |
|
| 54 |
+ if (typeStr == "waitFactor") {
|
|
| 55 |
+ waitFactor.parseParams(line.substr(pos)); |
|
| 56 |
+ continue; |
|
| 57 |
+ } |
|
| 58 |
+ if (typeStr == "waitTime") {
|
|
| 59 |
+ waitTime.parseParams(line.substr(pos)); |
|
| 60 |
+ continue; |
|
| 61 |
+ } |
|
| 44 | 62 |
if (typeStr == "tree") {
|
| 45 | 63 |
Tree tree; |
| 46 | 64 |
tree.parseParams(line.substr(pos)); |
| ... | ... |
@@ -54,13 +72,15 @@ void Config::parseFile(std::string const &configFileName) {
|
| 54 | 72 |
} |
| 55 | 73 |
} |
| 56 | 74 |
|
| 57 |
-Nice const & Config::getNice() const {
|
|
| 58 |
- return nice; |
|
| 59 |
-} |
|
| 75 |
+Nice const &Config::getNice() const { return nice; }
|
|
| 60 | 76 |
|
| 61 |
-TreeMap const & Config::getTrees() const {
|
|
| 62 |
- return trees; |
|
| 63 |
-} |
|
| 77 |
+Float const &Config::getSleepTime() const { return sleepTime; }
|
|
| 78 |
+ |
|
| 79 |
+Float const &Config::getWaitFactor() const { return waitFactor; }
|
|
| 80 |
+ |
|
| 81 |
+Float const &Config::getWaitTime() const { return waitTime; }
|
|
| 82 |
+ |
|
| 83 |
+TreeMap const &Config::getTrees() const { return trees; }
|
|
| 64 | 84 |
|
| 65 | 85 |
bool Config::setPermissions(Callback &callback) const {
|
| 66 | 86 |
for (auto const &path_tree : trees) {
|
| ... | ... |
@@ -0,0 +1,31 @@ |
| 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/Float.h> |
|
| 8 |
+ |
|
| 9 |
+#include <permissioner/StringUtils.h> |
|
| 10 |
+ |
|
| 11 |
+#include <sstream> |
|
| 12 |
+#include <stdexcept> |
|
| 13 |
+#include <string> |
|
| 14 |
+ |
|
| 15 |
+Float::Float(std::string const &name, float minVal, float maxVal, float defVal) |
|
| 16 |
+ : name(name), minVal(minVal), maxVal(maxVal), val(defVal) {}
|
|
| 17 |
+ |
|
| 18 |
+void Float::parseParams(std::string const ¶mStr) {
|
|
| 19 |
+ // format of paramStr is: <floating-point-value> |
|
| 20 |
+ std::string valStr; |
|
| 21 |
+ std::string::size_type pos = 0; |
|
| 22 |
+ StringUtils::getNextField(paramStr, pos, valStr, name); |
|
| 23 |
+ if (pos < paramStr.length()) {
|
|
| 24 |
+ std::stringstream msg; |
|
| 25 |
+ msg << "too many fields for \"" << name << "\" in \"" << paramStr << "\""; |
|
| 26 |
+ throw std::runtime_error(msg.str()); |
|
| 27 |
+ } |
|
| 28 |
+ val = StringUtils::str2floatRange(valStr, minVal, maxVal, name); |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+float Float::get() const { return val; }
|
| ... | ... |
@@ -58,3 +58,28 @@ int StringUtils::str2intRange(std::string const &str, int minVal, int maxVal, |
| 58 | 58 |
} |
| 59 | 59 |
return val; |
| 60 | 60 |
} |
| 61 |
+ |
|
| 62 |
+float StringUtils::str2float(std::string const &str, std::string const &name) {
|
|
| 63 |
+ char const *c_str = str.c_str(); |
|
| 64 |
+ char *end; |
|
| 65 |
+ float val = strtof(c_str, &end); |
|
| 66 |
+ if (end == c_str || *end != 0) {
|
|
| 67 |
+ std::stringstream msg; |
|
| 68 |
+ msg << "invalid floating-point value \"" << str << "\" for <" << name |
|
| 69 |
+ << "> field"; |
|
| 70 |
+ throw std::runtime_error(msg.str()); |
|
| 71 |
+ } |
|
| 72 |
+ return val; |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+float StringUtils::str2floatRange(std::string const &str, float minVal, |
|
| 76 |
+ float maxVal, std::string const &name) {
|
|
| 77 |
+ float val = str2float(str, name); |
|
| 78 |
+ if (val < minVal || val > maxVal) {
|
|
| 79 |
+ std::stringstream msg; |
|
| 80 |
+ msg << "value " << val << " of <" << name << "> field out of range " |
|
| 81 |
+ << minVal << " - " << maxVal; |
|
| 82 |
+ throw std::runtime_error(msg.str()); |
|
| 83 |
+ } |
|
| 84 |
+ return val; |
|
| 85 |
+} |
| ... | ... |
@@ -18,20 +18,24 @@ |
| 18 | 18 |
class DaemonCallback : public Callback {
|
| 19 | 19 |
public: |
| 20 | 20 |
DaemonCallback() : go_on(true) {}
|
| 21 |
- bool callback() { return go_on; }
|
|
| 21 |
+ bool callback(); |
|
| 22 |
+ std::chrono::duration<float, std::ratio<1>> sleepTime; |
|
| 23 |
+ template <class Rep, class Period> |
|
| 24 |
+ bool iterativeSleep(std::chrono::duration<Rep, Period> duration) const; |
|
| 22 | 25 |
bool go_on; |
| 23 | 26 |
}; |
| 24 | 27 |
|
| 25 |
-DaemonCallback daemonCallback; |
|
| 26 |
- |
|
| 27 |
-void sighandler(int) { daemonCallback.go_on = false; }
|
|
| 28 |
+bool DaemonCallback::callback() {
|
|
| 29 |
+ iterativeSleep(sleepTime); |
|
| 30 |
+ return go_on; |
|
| 31 |
+} |
|
| 28 | 32 |
|
| 29 | 33 |
// iterative sleep, watching go_on, returns whether sleep completed |
| 30 | 34 |
template <class Rep, class Period> |
| 31 |
-bool iterativeSleep(std::chrono::duration<Rep, Period> duration, |
|
| 32 |
- DaemonCallback &daemonCallback) {
|
|
| 33 |
- std::chrono::duration<int, std::milli> zero(0), step(100); |
|
| 34 |
- while (duration > zero && daemonCallback.go_on) {
|
|
| 35 |
+bool DaemonCallback::iterativeSleep( |
|
| 36 |
+ std::chrono::duration<Rep, Period> duration) const {
|
|
| 37 |
+ const std::chrono::duration<int, std::milli> zero(0), step(100); |
|
| 38 |
+ while (duration > zero && go_on) {
|
|
| 35 | 39 |
if (duration >= step) {
|
| 36 | 40 |
std::this_thread::sleep_for(step); |
| 37 | 41 |
duration -= step; |
| ... | ... |
@@ -43,6 +47,10 @@ bool iterativeSleep(std::chrono::duration<Rep, Period> duration, |
| 43 | 47 |
return duration <= zero; |
| 44 | 48 |
} |
| 45 | 49 |
|
| 50 |
+DaemonCallback daemonCallback; |
|
| 51 |
+ |
|
| 52 |
+void sighandler(int) { daemonCallback.go_on = false; }
|
|
| 53 |
+ |
|
| 46 | 54 |
int main(int argc, char const **argv) {
|
| 47 | 55 |
if (argc != 2) {
|
| 48 | 56 |
std::cerr << "usage: " << argv[0] << " <config file>" << std::endl; |
| ... | ... |
@@ -59,6 +67,13 @@ int main(int argc, char const **argv) {
|
| 59 | 67 |
return EXIT_FAILURE; |
| 60 | 68 |
} |
| 61 | 69 |
|
| 70 |
+ // get timing from config |
|
| 71 |
+ std::chrono::duration<float, std::ratio<1>> sleepTime( |
|
| 72 |
+ config.getSleepTime().get()); |
|
| 73 |
+ float waitFactor = config.getWaitFactor().get(); |
|
| 74 |
+ std::chrono::duration<float, std::ratio<1>> waitTime( |
|
| 75 |
+ config.getWaitTime().get()); |
|
| 76 |
+ |
|
| 62 | 77 |
// catch signals to exit properly on Ctrl-C and so on |
| 63 | 78 |
signal(SIGINT, sighandler); |
| 64 | 79 |
signal(SIGPIPE, sighandler); |
| ... | ... |
@@ -70,6 +85,9 @@ int main(int argc, char const **argv) {
|
| 70 | 85 |
// set nicecess of process |
| 71 | 86 |
config.getNice().apply(); |
| 72 | 87 |
|
| 88 |
+ // set sleep time after each file in daemon callback |
|
| 89 |
+ daemonCallback.sleepTime = sleepTime; |
|
| 90 |
+ |
|
| 73 | 91 |
// continuously set ownership and permissions |
| 74 | 92 |
int ret = EXIT_SUCCESS; |
| 75 | 93 |
while (daemonCallback.go_on) {
|
| ... | ... |
@@ -92,10 +110,8 @@ int main(int argc, char const **argv) {
|
| 92 | 110 |
std::cout << "permissionerd (" << configFileName << "): took "
|
| 93 | 111 |
<< duration.count() << " s" << std::endl; |
| 94 | 112 |
|
| 95 |
- // sleep 10 times as long as the work took plus one second |
|
| 96 |
- auto sleep_time = |
|
| 97 |
- 10 * duration + std::chrono::duration<int, std::ratio<1>>(1); |
|
| 98 |
- if (! iterativeSleep(sleep_time, daemonCallback)) {
|
|
| 113 |
+ // wait after tree traversal |
|
| 114 |
+ if (!daemonCallback.iterativeSleep(waitFactor * duration + waitTime)) {
|
|
| 99 | 115 |
break; |
| 100 | 116 |
} |
| 101 | 117 |
|
| ... | ... |
@@ -34,6 +34,25 @@ int testEmpty() {
|
| 34 | 34 |
ret = EXIT_FAILURE; |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
+ float sleepTime = config.getSleepTime().get(); |
|
| 38 |
+ if (sleepTime != 1.0e-6f) {
|
|
| 39 |
+ std::cerr << "unexpected sleepTime " << sleepTime << ", expected " |
|
| 40 |
+ << 1.0e-6f << std::endl; |
|
| 41 |
+ ret = EXIT_FAILURE; |
|
| 42 |
+ } |
|
| 43 |
+ float waitFactor = config.getWaitFactor().get(); |
|
| 44 |
+ if (waitFactor != 10.0f) {
|
|
| 45 |
+ std::cerr << "unexpected waitFactor " << waitFactor << ", expected " |
|
| 46 |
+ << 10.0f << std::endl; |
|
| 47 |
+ ret = EXIT_FAILURE; |
|
| 48 |
+ } |
|
| 49 |
+ float waitTime = config.getWaitTime().get(); |
|
| 50 |
+ if (waitTime != 1.0f) {
|
|
| 51 |
+ std::cerr << "unexpected waitTime " << waitTime << ", expected " << 1.0f |
|
| 52 |
+ << std::endl; |
|
| 53 |
+ ret = EXIT_FAILURE; |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 37 | 56 |
TreeMap const &treeMap = config.getTrees(); |
| 38 | 57 |
if (treeMap.size() != 0) {
|
| 39 | 58 |
std::cerr << "unexpected trees: " << treeMap.size() << std::endl; |
| ... | ... |
@@ -103,6 +122,34 @@ int testNice() {
|
| 103 | 122 |
return ret; |
| 104 | 123 |
} |
| 105 | 124 |
|
| 125 |
+int testSleepWait() {
|
|
| 126 |
+ Config config; |
|
| 127 |
+ config.parseFile("sleep_wait.cfg");
|
|
| 128 |
+ |
|
| 129 |
+ int ret = EXIT_SUCCESS; |
|
| 130 |
+ |
|
| 131 |
+ float sleepTime = config.getSleepTime().get(); |
|
| 132 |
+ if (sleepTime != 42.42e-3f) {
|
|
| 133 |
+ std::cerr << "unexpected sleepTime " << sleepTime << ", expected " |
|
| 134 |
+ << 42.42e-3f << std::endl; |
|
| 135 |
+ ret = EXIT_FAILURE; |
|
| 136 |
+ } |
|
| 137 |
+ float waitFactor = config.getWaitFactor().get(); |
|
| 138 |
+ if (waitFactor != 5.5f) {
|
|
| 139 |
+ std::cerr << "unexpected waitFactor " << waitFactor << ", expected " |
|
| 140 |
+ << 5.5f << std::endl; |
|
| 141 |
+ ret = EXIT_FAILURE; |
|
| 142 |
+ } |
|
| 143 |
+ float waitTime = config.getWaitTime().get(); |
|
| 144 |
+ if (waitTime != 23.23f) {
|
|
| 145 |
+ std::cerr << "unexpected waitTime " << waitTime << ", expected " << 23.23f |
|
| 146 |
+ << std::endl; |
|
| 147 |
+ ret = EXIT_FAILURE; |
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+ return ret; |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 106 | 153 |
bool checkTrees(TreeMap const &treeMap, std::string const &rel_path, |
| 107 | 154 |
boost::optional<std::string> user, |
| 108 | 155 |
boost::optional<std::string> group, mode_t setMode, |
| ... | ... |
@@ -195,6 +242,7 @@ int main() {
|
| 195 | 242 |
int ret = EXIT_SUCCESS; |
| 196 | 243 |
merge_ret(ret, testEmpty()); |
| 197 | 244 |
merge_ret(ret, testNice()); |
| 245 |
+ merge_ret(ret, testSleepWait()); |
|
| 198 | 246 |
merge_ret(ret, testTrees()); |
| 199 | 247 |
return ret; |
| 200 | 248 |
} |
| 201 | 249 |