SNode.C
Loading...
Searching...
No Matches
Daemon.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "utils/Daemon.h"
21
22#ifndef DOXYGEN_SHOULD_SKIP_THIS
23
24#include "core/system/poll.h"
25
26#include <cerrno>
27#include <cstdio>
28#include <cstring>
29#include <filesystem>
30#include <fstream>
31#include <grp.h>
32#include <pwd.h>
33#include <sys/syscall.h>
34
35#endif // DOXYGEN_SHOULD_SKIP_THIS
36
37namespace utils {
38
42
45
49
52
55 throw DaemonFailure("Pid file '" + pidFileName + "' exists. Daemon already running?");
56 }
57 errno = 0;
58
59 /* Fork off the parent process */
60 pid_t pid = fork();
61 if (pid < 0) {
62 /* An error occurred */
63 throw DaemonError("First fork()");
64 }
65 if (pid > 0) {
66 /* Success: Let the parent terminate */
67 throw DaemonSignaled("Lead new session", pid);
68 }
69
70 if (setsid() < 0) {
71 /* On success: The child process becomes session leader */
72 throw DaemonError("setsid()");
73 }
74
75 if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
76 /* Ignore signal sent from parent to child process */
77 throw DaemonError("signal()");
78 }
79
80 /* Fork off for the second time*/
81 pid = fork();
82 if (pid < 0) {
83 /* An error occurred */
84 throw DaemonError("Second fork()");
85 }
86 if (pid > 0) {
87 /* Success: Let the second parent terminate */
89
90 if (!pidFile.good()) {
92 throw DaemonError("Writing pid file '" + pidFileName);
93 }
94 pidFile << pid << std::endl;
95 pidFile.close();
96
97 throw DaemonSignaled("Drop session lead", pid);
98 }
99
100 struct passwd* pw = nullptr;
101 struct group* gr = nullptr;
102
103 if (((void) (errno = 0), gr = getgrnam(groupName.c_str())) == nullptr) {
104 if (errno != 0) {
105 throw DaemonError("getgrnam()");
106 }
107 throw DaemonFailure("getgrname() group not existing");
108 }
109 if (setegid(gr->gr_gid) != 0) {
110 throw DaemonError("setegid()");
111 }
112 if (((void) (errno = 0), (pw = getpwnam(userName.c_str())) == nullptr)) {
113 if (errno != 0) {
114 throw DaemonError("getpwnam()");
115 }
116 throw DaemonFailure("getpwnam() user not existing");
117 }
118 if (seteuid(pw->pw_uid) != 0) {
119 throw DaemonError("seteuid()");
120 } /* Set new file permissions */
121 umask(0);
122 chdir("/");
123
127
128 if (std::freopen("/dev/null", "r", stdin) == nullptr) {
129 }
130 if (std::freopen("/dev/null", "w+", stdout) == nullptr) {
131 }
132 if (std::freopen("/dev/null", "w+", stderr) == nullptr) {
133 }
134 }
135
137 if (pidFileName.empty()) {
138 throw DaemonFailure("No pid file given");
139 } /* Try to read PID of daemon to from lockfile and kill the daemon */
141
142 if (!pidFile.good()) {
143 pidFile.close();
144 throw DaemonError("Reading pid file '" + pidFileName + "'");
145 }
146 pid_t pid = 0;
147 pidFile >> pid;
148 pidFile.close();
149
150 const int pidfd = static_cast<int>(syscall(SYS_pidfd_open, pid, 0)); // NOLINT
151
152 if (pidfd == -1) {
154 throw DaemonFailure("Daemon not running");
155 }
156 if (kill(pid, SIGTERM) != 0) {
157 throw DaemonError("kill()");
158 }
159 struct pollfd pollfd{};
160 pollfd.fd = pidfd;
162
163 const int ready = core::system::poll(&pollfd, 1, 5000);
164 close(pidfd);
165
166 if (ready == -1) {
167 throw DaemonError("poll()");
168 }
169 if (ready == 0) {
170 kill(pid, SIGKILL);
172 throw DaemonFailure("Daemon not responding - killed");
173 }
174
175 return pid;
176 }
177
179 (void) seteuid(getuid()); // In case we are here seteguid can not fail
180 (void) setegid(getgid()); // In case we are here setegid can not fail
181 std::filesystem::remove(pidFileName); // In case we are here std::Filesystem::remove can not fail
182 }
183
188
191
193 return pid;
194 }
195
196} // namespace utils
Definition Config.h:37