128 {
129 bool proceed = true;
130
133
135
136 uid_t euid = 0;
137 struct passwd* pw = nullptr;
138 struct group* gr = nullptr;
139
140 if ((pw = getpwuid(getuid())) == nullptr) {
141 proceed = false;
142 } else if ((gr = getgrgid(pw->pw_gid)) == nullptr) {
143 proceed = false;
144 } else if ((euid = geteuid()) == 0) {
148 } else {
149 const char* homedir = nullptr;
150 if ((homedir = std::getenv("XDG_CONFIG_HOME")) == nullptr) {
151 if ((homedir = std::getenv("HOME")) == nullptr) {
152 homedir = pw->pw_dir;
153 }
154 }
155
156 if (homedir != nullptr) {
158 logDirectory = std::string(homedir) +
"/.local/log/snode.c";
159 pidDirectory = std::string(homedir) +
"/.local/run/snode.c";
160 } else {
161 proceed = false;
162 }
163 }
164
167 std::filesystem::permissions(
169 (std::filesystem::perms::owner_all | std::filesystem::perms::group_read | std::filesystem::perms::group_exec) &
170 ~std::filesystem::perms::others_all);
171 if (geteuid() == 0) {
172 struct group* gr = nullptr;
173 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
175 std::cout <<
"Warning: Can not set group ownership of '" <<
configDirectory
176 << "' to 'snodec':" << strerror(errno) << std::endl;
177 }
178 } else {
179 std::cout << "Error: Can not find group 'snodec'. Add it using groupadd or addgroup" << std::endl;
180 std::cout << " and add the current user to this group." << std::endl;
182 proceed = false;
183 }
184 }
185 } else {
186 std::cout <<
"Error: Can not create directory '" <<
configDirectory <<
"'" << std::endl;
187 proceed = false;
188 }
189 }
190
191 if (proceed && !std::filesystem::exists(
logDirectory)) {
192 if (std::filesystem::create_directories(
logDirectory)) {
194 (std::filesystem::perms::owner_all | std::filesystem::perms::group_all) &
195 ~std::filesystem::perms::others_all);
196 if (geteuid() == 0) {
197 struct group* gr = nullptr;
198 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
199 if (chown(
logDirectory.c_str(), euid, gr->gr_gid) < 0) {
200 std::cout <<
"Warning: Can not set group ownership of '" <<
logDirectory <<
"' to 'snodec':" << strerror(errno)
201 << std::endl;
202 }
203 } else {
204 std::cout << "Error: Can not find group 'snodec'. Add it using groupadd or addgroup" << std::endl;
205 std::cout << " and add the current user to this group." << std::endl;
207 proceed = false;
208 }
209 }
210 } else {
211 std::cout <<
"Error: Can not create directory '" <<
logDirectory <<
"'" << std::endl;
212 proceed = false;
213 }
214 }
215
216 if (proceed && !std::filesystem::exists(
pidDirectory)) {
217 if (std::filesystem::create_directories(
pidDirectory)) {
219 (std::filesystem::perms::owner_all | std::filesystem::perms::group_all) &
220 ~std::filesystem::perms::others_all);
221 if (geteuid() == 0) {
222 struct group* gr = nullptr;
223 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
224 if (chown(
pidDirectory.c_str(), euid, gr->gr_gid) < 0) {
225 std::cout <<
"Warning: Can not set group ownership of '" <<
pidDirectory <<
"' to 'snodec':" << strerror(errno)
226 << std::endl;
227 }
228 } else {
229 std::cout << "Error: Can not find group 'snodec'. Add it using groupadd or addgroup." << std::endl;
230 std::cout << " and add the current user to this group." << std::endl;
232 proceed = false;
233 }
234 }
235 } else {
236 std::cout <<
"Error: Can not create directory '" <<
pidDirectory <<
"'" << std::endl;
237 proceed = false;
238 }
239 }
240
241 if (proceed) {
243
245 "' powered by SNode.C\n"
246 "(C) 2020-2025 Volker Christian <me@vchrist.at>\n"
247 "https://github.com/SNodeC/snode.c");
248
250 "-c,--config-file",
252 "Read a config file",
253 false)
254 ->take_all()
255 ->type_name("configfile")
256 ->check(!CLI::ExistingDirectory);
257
258 app->add_option_function<std::string>(
259 "-w,--write-config",
260 []([[maybe_unused]] const std::string& configFile) {
262 },
263 "Write config file and exit")
264 ->configurable(false)
266 ->type_name("[configfile]")
267 ->check(!CLI::ExistingDirectory)
268 ->expected(0, 1);
269
271 "-k,--kill",
272 "Kill running daemon")
273 ->configurable(false)
274 ->disable_flag_override();
275
277 "-i,--instance-alias",
278 "Make an instance also known as an alias in configuration files")
279 ->configurable(false)
280 ->type_name("instance=instance_alias [instance=instance_alias [...]]")
281 ->each([](const std::string& item) {
282 const auto it = item.find('=');
283 if (it != std::string::npos) {
284 aliases[item.substr(0, it)] = item.substr(it + 1);
285 } else {
286 throw CLI::ConversionError("Can not convert '" + item + "' to a 'instance=instance_alias' pair");
287 }
288 });
289
291
293 "-l,--log-level",
294 "Log level")
295 ->default_val(4)
296 ->type_name("level")
297 ->check(CLI::Range(0, 6))
298 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
299
301 "-v,--verbose-level",
302 "Verbose level")
303 ->default_val(2)
304 ->type_name("level")
305 ->check(CLI::Range(0, 10))
306 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
307
309 "-q{true},!-u,--quiet{true}",
310 "Quiet mode")
311 ->default_val("false")
312 ->type_name("bool")
313 ->check(CLI::IsMember({"true", "false"}))
314 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
315
317 "--log-file",
318 "Log file path")
320 ->type_name("logfile")
321 ->check(!CLI::ExistingDirectory)
322 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
323
325 "-e{true},!-n,--enforce-log-file{true}",
326 "Enforce writing of logs to file for foreground applications")
327 ->default_val("false")
328 ->type_name("bool")
329 ->check(CLI::IsMember({"true", "false"}))
330 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
331
333 "-d{true},!-f,--daemonize{true}",
334 "Start application as daemon")
335 ->default_val("false")
336 ->type_name("bool")
337 ->check(CLI::IsMember({"true", "false"}))
338 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
339
341 "--user-name",
342 "Run daemon under specific user permissions")
343 ->default_val(pw->pw_name)
344 ->type_name("username")
346 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
347
349 "--group-name",
350 "Run daemon under specific group permissions")
351 ->default_val(gr->gr_name)
352 ->type_name("groupname")
354 ->group(
app->get_formatter()->get_label(
"Persistent Options"));
355
357
358 app->set_version_flag(
"--version",
"1.0-rc1",
"Framework version");
360 }
361
362 return proceed;
363 }
static CLI::App * addStandardFlags(CLI::App *app)
static std::string configDirectory
static CLI::Option * groupNameOpt
static CLI::App * addHelp(CLI::App *app)
static CLI::Option * quietOpt
static std::string pidDirectory
static CLI::Option * enforceLogFileOpt
static std::string logDirectory
static CLI::Option * userNameOpt