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/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "utils/Daemon.h"
43
44#ifndef DOXYGEN_SHOULD_SKIP_THIS
45
46#include "core/system/poll.h"
47
48#include <cerrno>
49#include <cstdio>
50#include <cstring>
51#include <filesystem>
52#include <fstream>
53#include <grp.h>
54#include <pwd.h>
55#include <sys/syscall.h>
56
57#endif // DOXYGEN_SHOULD_SKIP_THIS
58
59namespace utils {
60
61 DaemonFailure::DaemonFailure(const std::string& failureMessage)
62 : std::runtime_error(failureMessage) {
63 }
64
66 }
67
68 DaemonError::DaemonError(const std::string& errorMessage)
69 : DaemonFailure(errorMessage + ": " + std::strerror(errno)) {
70 }
71
73 }
74
75 void Daemon::startDaemon(const std::string& pidFileName, const std::string& userName, const std::string& groupName) {
76 if (std::filesystem::exists(pidFileName)) {
77 throw DaemonFailure("Pid file '" + pidFileName + "' exists. Daemon already running?");
78 }
79 errno = 0;
80
81 /* Fork off the parent process */
82 pid_t pid = fork();
83 if (pid < 0) {
84 /* An error occurred */
85 throw DaemonError("First fork()");
86 }
87 if (pid > 0) {
88 /* Success: Let the parent terminate */
89 throw DaemonSignaled("Lead new session", pid);
90 }
91
92 if (setsid() < 0) {
93 /* On success: The child process becomes session leader */
94 throw DaemonError("setsid()");
95 }
96
97 if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
98 /* Ignore signal sent from parent to child process */
99 throw DaemonError("signal()");
100 }
101
102 /* Fork off for the second time*/
103 pid = fork();
104 if (pid < 0) {
105 /* An error occurred */
106 throw DaemonError("Second fork()");
107 }
108 if (pid > 0) {
109 /* Success: Let the second parent terminate */
110 std::ofstream pidFile(pidFileName, std::ofstream::out);
111
112 if (!pidFile.good()) {
113 kill(pid, SIGTERM);
114 throw DaemonError("Writing pid file '" + pidFileName);
115 }
116 pidFile << pid << std::endl;
117 pidFile.close();
118
119 throw DaemonSignaled("Drop session lead", pid);
120 }
121
122 struct passwd* pw = nullptr;
123 struct group* gr = nullptr;
124
125 if (((void) (errno = 0), gr = getgrnam(groupName.c_str())) == nullptr) {
126 if (errno != 0) {
127 throw DaemonError("getgrnam()");
128 }
129 throw DaemonFailure("getgrname() group not existing");
130 }
131 if (setegid(gr->gr_gid) != 0) {
132 throw DaemonError("setegid()");
133 }
134 if (((void) (errno = 0), (pw = getpwnam(userName.c_str())) == nullptr)) {
135 if (errno != 0) {
136 throw DaemonError("getpwnam()");
137 }
138 throw DaemonFailure("getpwnam() user not existing");
139 }
140 if (seteuid(pw->pw_uid) != 0) {
141 throw DaemonError("seteuid()");
142 } /* Set new file permissions */
143 umask(0);
144 chdir("/");
145
146 close(STDIN_FILENO);
147 close(STDOUT_FILENO);
148 close(STDERR_FILENO);
149
150 if (std::freopen("/dev/null", "r", stdin) == nullptr) {
151 }
152 if (std::freopen("/dev/null", "w+", stdout) == nullptr) {
153 }
154 if (std::freopen("/dev/null", "w+", stderr) == nullptr) {
155 }
156 }
157
158 pid_t Daemon::stopDaemon(const std::string& pidFileName) {
159 if (pidFileName.empty()) {
160 throw DaemonFailure("No pid file given");
161 } /* Try to read PID of daemon to from lockfile and kill the daemon */
162 std::ifstream pidFile(pidFileName, std::ifstream::in);
163
164 if (!pidFile.good()) {
165 pidFile.close();
166 throw DaemonError("Reading pid file '" + pidFileName + "'");
167 }
168 pid_t pid = 0;
169 pidFile >> pid;
170 pidFile.close();
171
172 const int pidfd = static_cast<int>(syscall(SYS_pidfd_open, pid, 0)); // NOLINT
173
174 if (pidfd == -1) {
175 erasePidFile(pidFileName);
176 throw DaemonFailure("Daemon not running");
177 }
178 if (kill(pid, SIGTERM) != 0) {
179 throw DaemonError("kill()");
180 }
181 struct pollfd pollfd{};
182 pollfd.fd = pidfd;
183 pollfd.events = POLLIN;
184
185 const int ready = core::system::poll(&pollfd, 1, 5000);
186 close(pidfd);
187
188 if (ready == -1) {
189 throw DaemonError("poll()");
190 }
191 if (ready == 0) {
192 kill(pid, SIGKILL);
193 erasePidFile(pidFileName);
194 throw DaemonFailure("Daemon not responding - killed");
195 }
196
197 return pid;
198 }
199
200 void Daemon::erasePidFile(const std::string& pidFileName) {
201 (void) seteuid(getuid()); // In case we are here seteguid can not fail
202 (void) setegid(getgid()); // In case we are here setegid can not fail
203 std::filesystem::remove(pidFileName); // In case we are here std::Filesystem::remove can not fail
204 }
205
206 DaemonSignaled::DaemonSignaled(const std::string& message, pid_t pid)
207 : std::runtime_error(message)
208 , pid(pid) {
209 }
210
212 }
213
214 pid_t DaemonSignaled::getPid() const {
215 return pid;
216 }
217
218} // namespace utils
~DaemonError() override
Definition Daemon.cpp:72
DaemonError(const std::string &errorMessage)
Definition Daemon.cpp:68
~DaemonFailure() override
Definition Daemon.cpp:65
DaemonFailure(const std::string &failureMessage)
Definition Daemon.cpp:61
~DaemonSignaled() override
Definition Daemon.cpp:211
pid_t getPid() const
Definition Daemon.cpp:214
DaemonSignaled(const std::string &message, pid_t pid)
Definition Daemon.cpp:206
static void startDaemon(const std::string &pidFileName, const std::string &userName, const std::string &groupName)
Definition Daemon.cpp:75
static pid_t stopDaemon(const std::string &pidFileName)
Definition Daemon.cpp:158
static void erasePidFile(const std::string &pidFileName)
Definition Daemon.cpp:200