finish binaries, add readme
Stefan Schuermans

Stefan Schuermans commited on 2020-09-17 19:19:25
Showing 6 changed files, with 251 additions and 2 deletions.

... ...
@@ -28,5 +28,6 @@ add_custom_target(
28 28
 )
29 29
 
30 30
 add_subdirectory(libpermissioner)
31
+add_subdirectory(permissionerc)
31 32
 add_subdirectory(permissionerd)
32 33
 add_subdirectory(tests)
... ...
@@ -0,0 +1,159 @@
1
+# Permissioner: Setting File Ownerships and Permissions
2
+
3
+The unix tools `chown` and `chmod` allow to set ownership and permissions of
4
+files and entire directory trees. However, multiple calls to those tools
5
+are needed when setting complex ownerships and permissions of nested directory
6
+trees. In case the change of ownerships and permissions shall be very fast for
7
+file, e.g., because the directory tree is accessed in parallel to the change,
8
+using multiple calls is not suitable.
9
+
10
+For example, let's assume, multiple users write files to a shared directory
11
+`shared/` in order to implement a primitive ad-hoc file sharing. The users
12
+all have the group `fileshare`. All files copied to the shared directory
13
+shall be readable, writable and deletable by all other users in the group.
14
+However, there is one directory, called `shared/perm`, in which files should
15
+stay permanently and be only readable for all users. This could be implemented
16
+by executing the following commands periodically in a `cron`-job:
17
+
18
+```
19
+chown -R nobody:fileshare shared
20
+chmod -R ug+rwX shared
21
+chmod -R g-w shared/perm
22
+```
23
+
24
+However, while the sequence of the three commands is executing, strange and
25
+unwanted ownerships and permissions may occur. If the directory tree is large,
26
+it can lead to problems accessing the files in parallel.
27
+
28
+Permissioner can help to reduce the problems by touching each file only once,
29
+and setting its ownership and permissions very quickly, before advancing to the
30
+next file.
31
+
32
+Using the configuration file `fileshare.cfg` with the content
33
+
34
+```
35
+tree nobody fileshare ug+rwX shared
36
+tree nobody fileshare u+rwX,g+wX,g-w shared/perm
37
+```
38
+
39
+in the call
40
+
41
+```
42
+bin/permissionerc fileshare.cfg
43
+```
44
+
45
+has the same effect, but achieves the same outcome while touching every file
46
+just once - thus avoiding the transient strange state of files.
47
+
48
+This is only a simple example to illustrate the functionality of
49
+permissioner. There are various other tools to properly implement a file
50
+sharing service. The sketched setup is only an ad-hoc hack and not a proper
51
+solution.
52
+
53
+Situations in which `chown` and `chmod` are not sufficient are no very common.
54
+Thus, permissioner is a very specific tool for a very specific use case. If
55
+you are not sure if you should use permissioner, after reading the above
56
+example, you should probably stick with the Unix tools `chown` and `chmod`.
57
+
58
+## Building
59
+
60
+Permissioner is developed on Debian Linux 10 "buster".
61
+
62
+Install the dependencies:
63
+
64
+```
65
+apt-get install -y build-essential cmake gcc g++ ninja-build \
66
+```
67
+
68
+Change to the directory of this `REAMDE.md` file.
69
+
70
+Configure a build directory:
71
+
72
+```
73
+mkdir build
74
+cd build
75
+cmake -G Ninja -D CMAKE_BUILD_TYPE=Release ..
76
+```
77
+
78
+Build:
79
+
80
+```
81
+ninja
82
+```
83
+
84
+Run tests:
85
+
86
+```
87
+ctest
88
+```
89
+
90
+## Config File
91
+
92
+The configuration file lists directory trees and the ownerships and permissions
93
+to set for them. If some of the specified trees are nested within each other,
94
+the nested tree(s) is/are excluded from the containing tree(s).
95
+
96
+The syntax of the config file is line-based. Each line defines a directory tree
97
+and the ownerships and permissions.
98
+
99
+Syntax:
100
+
101
+* Each line: `tree <user> <group> <permissions> <directory>`
102
+* `<user>`: User name to set as user/owner, `-` to not change the user/owner.
103
+* `<group>`: Group name to set as group, `-` to not change the group.
104
+* `<permissions>`: Comma-separated list of permission settings.
105
+   * `<perm setting>[,<perm setting>[,<...>]]`
106
+* `<perm setting>`: Setting (`=`), adding (`+`) or removing (`-`) permissions.
107
+   * `<who>[=+-]<what>`
108
+* `<who>`: For whom to change the permissions. Any combination of:
109
+   * `u`: User.
110
+   * `g`: Group.
111
+   * `o`: Others.
112
+* `<what>`: Which permissions to change.
113
+   * `r`: Reading.
114
+   * `w`: Writing.
115
+   * `x`: Executing for files, browsing for directories.
116
+   * `X`: Like `x` if `x` set for user/owner of the file.
117
+* `<directory>`: Absolute or relative directory name.
118
+   * Defines the base of a directory tree to process.
119
+   * Relative directory names are relative to the woking directory of
120
+     `permissionerc` or `permissionerd`.
121
+
122
+Example:
123
+
124
+```
125
+tree nobody fileshare ug+rwX shared
126
+tree nobody fileshare u+rwX,g+wX,g-w shared/perm
127
+```
128
+
129
+## Permissioner Client
130
+
131
+The permissioner client is a simple binary that reads the config file and
132
+sets the ownerships and permissions according to the directory tress configured
133
+in it once.
134
+
135
+Call syntax:
136
+
137
+```
138
+bin/permissionerc <config file>
139
+```
140
+
141
+## Permissioner Daemon
142
+
143
+The permissioner daemon set ownerships and permissions repeatedly.
144
+
145
+Call syntax:
146
+
147
+```
148
+bin/permissionerd <config file>
149
+```
150
+
151
+The daemon will execute the following actions in a loop:
152
+
153
+* Set ownwerships and permissions according to the config file,
154
+  while measuring the time.
155
+* Wait (sleep) for 10 times as long as it took plus one second.
156
+* Repeat.
157
+
158
+The daemon continues to run until it receives an interrupt (`SIGINT`) or
159
+termination (`SIGTERM`) signal.
... ...
@@ -0,0 +1,13 @@
1
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
2
+
3
+add_executable(
4
+  permissionerc
5
+  src/permissionerc.cpp
6
+)
7
+
8
+target_link_libraries(
9
+  permissionerc
10
+  PUBLIC
11
+  permissioner
12
+)
13
+
... ...
@@ -0,0 +1,25 @@
1
+#include <permissioner/Config.h>
2
+
3
+#include <cstdlib>
4
+#include <iostream>
5
+#include <stdexcept>
6
+#include <string>
7
+
8
+int main(int argc, char const **argv) {
9
+  if (argc != 2) {
10
+    std::cerr << "usage: " << argv[0] << " <config file>" << std::endl;
11
+    return EXIT_FAILURE;
12
+  }
13
+  std::string configFileName(argv[1]);
14
+
15
+  try {
16
+    Config config;
17
+    config.parseFile(configFileName);
18
+    config.setPermissions();
19
+  } catch (std::exception const &e) {
20
+    std::cerr << "error: " << e.what() << std::endl;
21
+    return EXIT_FAILURE;
22
+  }
23
+
24
+  return EXIT_SUCCESS;
25
+}
... ...
@@ -1,3 +1,5 @@
1
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
2
+
1 3
 add_executable(
2 4
   permissionerd
3 5
   src/permissionerd.cpp
... ...
@@ -1,9 +1,18 @@
1 1
 #include <permissioner/Config.h>
2 2
 
3
+#include <chrono>
4
+#include <csignal>
3 5
 #include <cstdlib>
4 6
 #include <iostream>
5 7
 #include <stdexcept>
6 8
 #include <string>
9
+#include <thread>
10
+
11
+static int go_on = 1;
12
+
13
+void sighandler(int) {
14
+  go_on = 0;
15
+}
7 16
 
8 17
 int main(int argc, char const **argv) {
9 18
   if (argc != 2) {
... ...
@@ -12,13 +21,53 @@ int main(int argc, char const **argv) {
12 21
   }
13 22
   std::string configFileName(argv[1]);
14 23
 
15
-  try {
24
+  // load configuration
16 25
   Config config;
26
+  try {
17 27
     config.parseFile(configFileName);
18 28
   } catch (std::exception const &e) {
19 29
     std::cerr << "error: " << e.what() << std::endl;
20 30
     return EXIT_FAILURE;
21 31
   }
22 32
 
23
-  return EXIT_SUCCESS;
33
+  // catch signals to exit properly on Ctrl-C and so on
34
+  signal(SIGINT, sighandler);
35
+  signal(SIGPIPE, sighandler);
36
+  signal(SIGQUIT, sighandler);
37
+  signal(SIGTERM, sighandler);
38
+
39
+  std::cout << "permissionerd (" << configFileName << ") starting"
40
+            << std::endl;
41
+
42
+  // continuously set ownership and permissions
43
+  int ret = EXIT_SUCCESS;
44
+  while (go_on) {
45
+
46
+    // set owneship and permissions, measure time it takes
47
+    std::cout << "permissionerd (" << configFileName
48
+              << "): setting ownership and permissions" << std::endl;
49
+    auto begin = std::chrono::steady_clock::now();
50
+    try {
51
+      config.setPermissions();
52
+    } catch (std::exception const &e) {
53
+      std::cerr << "error: " << e.what() << std::endl;
54
+      ret = EXIT_FAILURE;
55
+      break;
56
+    }
57
+    auto end = std::chrono::steady_clock::now();
58
+    std::chrono::duration<float, std::ratio<1>> duration = end - begin;
59
+    std::cout << "permissionerd (" << configFileName << "): took "
60
+              << duration.count() << " s" << std::endl;
61
+
62
+    // sleep 10 times as long as the work took plus one second
63
+    auto sleep_time = 10 * duration
64
+                    + std::chrono::duration<int, std::ratio<1>>(1);
65
+    std::this_thread::sleep_for(sleep_time);
66
+
67
+  } // while (go_on)
68
+
69
+  std::cout << "permissionerd (" << configFileName << ") shutting down"
70
+            << std::endl;
71
+
72
+  return ret;
24 73
 }
25 74