-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.cpp
345 lines (296 loc) · 9.16 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// Copyright (c) 2017 Deep Aggarwal
#include <getopt.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
static char *applicationName = NULL;
static char *pidFileName = NULL;
static int pidFd = -1;
/**
* @brief Callback function for handling signals.
*
* @param sig Identifier of a signal
*/
void handleSignal(int sig) {
if (sig == SIGINT) {
syslog(LOG_INFO, "Stopping %s", applicationName);
// Unlock and close lockfile
if (pidFd != -1) {
lockf(pidFd, F_ULOCK, 0);
close(pidFd);
}
// Delete lockfile
if (pidFileName != NULL) {
unlink(pidFileName);
}
// Reset signal handling to default behavior
signal(SIGINT, SIG_DFL);
}
}
static uid_t getUserID(const char *name) {
struct passwd pwentry;
struct passwd *result;
char *buf;
size_t bufsize;
int s;
// Call to sysconf(_SC_GETPW_R_SIZE_MAX) returns either -1
// without changing errno or an initial value suggested for
// the size for buf
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) {
// Should be more than enough
bufsize = 16384;
}
buf = new char[bufsize];
if (buf == NULL) {
exit(EXIT_FAILURE);
}
s = getpwnam_r(name, &pwentry, buf, bufsize, &result);
if (result == NULL) {
if (s == 0) {
std::string sname = name;
throw std::runtime_error("User \""+ sname +"\" is not found");
} else {
std::cerr << "Error in getpwnam_r(...)" << std::endl;
}
exit(EXIT_FAILURE);
}
return pwentry.pw_uid;
}
static gid_t getGroupID(const char *name) {
struct group grentry;
struct group *result;
char *buf;
size_t bufsize;
int s;
// Call to sysconf(_SC_GETGR_R_SIZE_MAX) returns either -1
// without changing errno or an initial value suggested for
// the size for buf
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
if (bufsize == -1) {
// Should be more than enough
bufsize = 16384;
}
buf = new char[bufsize];
if (buf == NULL) {
exit(EXIT_FAILURE);
}
s = getgrnam_r(name, &grentry, buf, bufsize, &result);
if (result == NULL) {
if (s == 0) {
std::string sname = name;
throw std::runtime_error("User \""+ sname +"\" is not found");
} else {
std::cerr << "Error in getgrnam_r(...)" << std::endl;
}
exit(EXIT_FAILURE);
}
return grentry.gr_gid;
}
/**
* @brief This function will daemonize this application
*/
static void daemonizeMe() {
pid_t pid = 0;
int fd;
// Fork off the parent process to ensures that
// the child process is not a process group leader, so
// that it is possible for that process tocreate
// a new session and become a session leader
pid = fork();
// If error occurred
if (pid < 0) {
exit(EXIT_FAILURE);
}
// On success, terminate the parent
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// On success, the child process becomes processgroup
// and session group leader. Since a controlling terminal
// is associated with a session, and this new session
// has not yet acquired a controlling terminal our
// process now has no controlling terminal,
// which is a good thing for daemons
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
// Ignore signal sent from child to parent process
signal(SIGCHLD, SIG_IGN);
// Fork off for the second time to ensure that
// the new child process is not a session leader,
// so it won't be able to (accidentally) allocate
// a controlling terminal, since daemons are not
// supposed to ever have a controlling terminal
pid = fork();
// If error occurred
if (pid < 0) {
exit(EXIT_FAILURE);
}
// On success, terminate the parent
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// Set new file permissions
//
// "0" means that new files this daemon will
// create will have read and write permission for everyone
// (0666 or -rw-rw-rw-), and new directories that
// this daemon will create will have read, write and
// search permissions for everyone (0777 or drwxrwxrwx)
// This measn that we have complete control over the
// permissions of anything we write
umask(0);
// Change the working directory to the root directory
// This is to ensure that our process doesn't keep any directory in use.
// Failure to do this could make it so that an administrator couldn't unmount
// a filesystem, because it was our current directory
if (chdir("/") != 0) {
exit(EXIT_FAILURE);
}
// Drop priviledges since running network based daemons
// with root permissions is considered to be a serious risk
if (getuid() == 0) {
// If here that means process is running as root,
// so drop the root privileges
uid_t userid = NULL;
gid_t groupid = NULL;
try {
// FIXME: Hard-coded since this is the user that we will deal with
userid = getUserID("mydaemon");
groupid = getGroupID("mydaemon");
} catch(const std::runtime_error& error) {
syslog(LOG_ERR, "\"mydaemon\" couldn't be found");
exit(EXIT_FAILURE);
}
if (setgid(groupid) != 0) {
exit(EXIT_FAILURE);
}
if (setuid(userid) != 0) {
exit(EXIT_FAILURE);
}
}
// If we try to get root privileges back, it
// should fail. If it doesn't fail, we exit with failure
if (setuid(0) != -1) {
exit(EXIT_FAILURE);
}
// Close all open file descriptors
for (fd = sysconf(_SC_OPEN_MAX); fd > 0; fd--) {
close(fd);
}
// Reopen stdin (fd = 0), stdout (fd = 1), stderr (fd = 2) as /dev/null
stdin = fopen("/dev/null", "r");
stdout = fopen("/dev/null", "w+");
stderr = fopen("/dev/null", "w+");
// Write PID of daemon to lockfile
if (pidFileName != NULL) {
char str[256];
pidFd = open(pidFileName, O_RDWR|O_CREAT, 0640);
if (pidFd < 0) {
// Can't open lockfile
exit(EXIT_FAILURE);
}
if (lockf(pidFd, F_TLOCK, 0) < 0) {
// Can't lock file
exit(EXIT_FAILURE);
}
// Get current PID
sprintf(str, "%d\n", getpid());
// Write PID to lockfile
write(pidFd, str, strlen(str));
}
}
/**
* @brief This function starts the application
*/
static void startApplication() {
syslog(LOG_NOTICE, "I am daemonizeMe and I am writing to my syslog");
while (true) {
// Run your server here
// For example, a server running in background as a daemon process
// listening for incoming requests from clients
}
}
/**
* @brief Free allocated memory
*/
static void cleanGlobalMemory() {
if (pidFileName) {
free(pidFileName);
}
return;
}
/**
* @brief Print usage on the stdout
*/
void printHelp(void) {
printf("\nUsage: %s [OPTIONS]\n\n", applicationName);
printf("Options:\n");
printf(" -h --help Print this help message\n");
printf(" -p --pid_file filename PID file used by this application\n");
printf(" -d --daemon Daemonize this application\n");
printf("\n");
}
int main(int argc, char **argv) {
// Get the application name
applicationName = argv[0];
static struct option commandLineOptions[] = {
{"pid_file", required_argument, 0, 'p'},
{"daemon", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{NULL, 0, 0, 0 }
};
// Process all the command line arguments
int getopt_longRV;
int optionIndex = 0;
int daemonize = 0; // 0 means do not daemonize
while ((getopt_longRV = getopt_long(argc,
argv,
"p:dh",
commandLineOptions,
&optionIndex)) != -1) {
switch (getopt_longRV) {
case 'p':
pidFileName = strdup(optarg);
break;
case 'd':
daemonize = 1;
break;
case 'h':
printHelp();
return EXIT_SUCCESS;
case '?':
printHelp();
return EXIT_FAILURE;
default:
break;
}
}
// If daemonize flag is passed
if (daemonize == 1) {
daemonizeMe();
}
// Open system log to write message to it
openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON);
syslog(LOG_INFO, "Started %s", applicationName);
// Handle SIGINT by this daemon
signal(SIGINT, handleSignal);
// Main function to start the application
startApplication();
// Write final system log and close the log
syslog(LOG_INFO, "Stopped %s", applicationName);
closelog();
// Free memory
cleanGlobalMemory();
return 0;
}