]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // daemon.cpp | |
3 | // ~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) | |
6 | // | |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #include <boost/asio/io_service.hpp> | |
12 | #include <boost/asio/ip/udp.hpp> | |
13 | #include <boost/asio/signal_set.hpp> | |
14 | #include <boost/array.hpp> | |
15 | #include <boost/bind.hpp> | |
16 | #include <ctime> | |
17 | #include <iostream> | |
18 | #include <syslog.h> | |
19 | #include <unistd.h> | |
20 | ||
21 | using boost::asio::ip::udp; | |
22 | ||
23 | class udp_daytime_server | |
24 | { | |
25 | public: | |
26 | udp_daytime_server(boost::asio::io_service& io_service) | |
27 | : socket_(io_service, udp::endpoint(udp::v4(), 13)) | |
28 | { | |
29 | start_receive(); | |
30 | } | |
31 | ||
32 | private: | |
33 | void start_receive() | |
34 | { | |
35 | socket_.async_receive_from( | |
36 | boost::asio::buffer(recv_buffer_), remote_endpoint_, | |
37 | boost::bind(&udp_daytime_server::handle_receive, this, _1)); | |
38 | } | |
39 | ||
40 | void handle_receive(const boost::system::error_code& ec) | |
41 | { | |
42 | if (!ec || ec == boost::asio::error::message_size) | |
43 | { | |
44 | using namespace std; // For time_t, time and ctime; | |
45 | time_t now = time(0); | |
46 | std::string message = ctime(&now); | |
47 | ||
48 | boost::system::error_code ignored_ec; | |
49 | socket_.send_to(boost::asio::buffer(message), | |
50 | remote_endpoint_, 0, ignored_ec); | |
51 | } | |
52 | ||
53 | start_receive(); | |
54 | } | |
55 | ||
56 | udp::socket socket_; | |
57 | udp::endpoint remote_endpoint_; | |
58 | boost::array<char, 1> recv_buffer_; | |
59 | }; | |
60 | ||
61 | int main() | |
62 | { | |
63 | try | |
64 | { | |
65 | boost::asio::io_service io_service; | |
66 | ||
67 | // Initialise the server before becoming a daemon. If the process is | |
68 | // started from a shell, this means any errors will be reported back to the | |
69 | // user. | |
70 | udp_daytime_server server(io_service); | |
71 | ||
72 | // Register signal handlers so that the daemon may be shut down. You may | |
73 | // also want to register for other signals, such as SIGHUP to trigger a | |
74 | // re-read of a configuration file. | |
75 | boost::asio::signal_set signals(io_service, SIGINT, SIGTERM); | |
76 | signals.async_wait( | |
77 | boost::bind(&boost::asio::io_service::stop, &io_service)); | |
78 | ||
79 | // Inform the io_service that we are about to become a daemon. The | |
80 | // io_service cleans up any internal resources, such as threads, that may | |
81 | // interfere with forking. | |
82 | io_service.notify_fork(boost::asio::io_service::fork_prepare); | |
83 | ||
84 | // Fork the process and have the parent exit. If the process was started | |
85 | // from a shell, this returns control to the user. Forking a new process is | |
86 | // also a prerequisite for the subsequent call to setsid(). | |
87 | if (pid_t pid = fork()) | |
88 | { | |
89 | if (pid > 0) | |
90 | { | |
91 | // We're in the parent process and need to exit. | |
92 | // | |
93 | // When the exit() function is used, the program terminates without | |
94 | // invoking local variables' destructors. Only global variables are | |
95 | // destroyed. As the io_service object is a local variable, this means | |
96 | // we do not have to call: | |
97 | // | |
98 | // io_service.notify_fork(boost::asio::io_service::fork_parent); | |
99 | // | |
100 | // However, this line should be added before each call to exit() if | |
101 | // using a global io_service object. An additional call: | |
102 | // | |
103 | // io_service.notify_fork(boost::asio::io_service::fork_prepare); | |
104 | // | |
105 | // should also precede the second fork(). | |
106 | exit(0); | |
107 | } | |
108 | else | |
109 | { | |
110 | syslog(LOG_ERR | LOG_USER, "First fork failed: %m"); | |
111 | return 1; | |
112 | } | |
113 | } | |
114 | ||
115 | // Make the process a new session leader. This detaches it from the | |
116 | // terminal. | |
117 | setsid(); | |
118 | ||
119 | // A process inherits its working directory from its parent. This could be | |
120 | // on a mounted filesystem, which means that the running daemon would | |
121 | // prevent this filesystem from being unmounted. Changing to the root | |
122 | // directory avoids this problem. | |
123 | chdir("/"); | |
124 | ||
125 | // The file mode creation mask is also inherited from the parent process. | |
126 | // We don't want to restrict the permissions on files created by the | |
127 | // daemon, so the mask is cleared. | |
128 | umask(0); | |
129 | ||
130 | // A second fork ensures the process cannot acquire a controlling terminal. | |
131 | if (pid_t pid = fork()) | |
132 | { | |
133 | if (pid > 0) | |
134 | { | |
135 | exit(0); | |
136 | } | |
137 | else | |
138 | { | |
139 | syslog(LOG_ERR | LOG_USER, "Second fork failed: %m"); | |
140 | return 1; | |
141 | } | |
142 | } | |
143 | ||
144 | // Close the standard streams. This decouples the daemon from the terminal | |
145 | // that started it. | |
146 | close(0); | |
147 | close(1); | |
148 | close(2); | |
149 | ||
150 | // We don't want the daemon to have any standard input. | |
151 | if (open("/dev/null", O_RDONLY) < 0) | |
152 | { | |
153 | syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m"); | |
154 | return 1; | |
155 | } | |
156 | ||
157 | // Send standard output to a log file. | |
158 | const char* output = "/tmp/asio.daemon.out"; | |
159 | const int flags = O_WRONLY | O_CREAT | O_APPEND; | |
160 | const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; | |
161 | if (open(output, flags, mode) < 0) | |
162 | { | |
163 | syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output); | |
164 | return 1; | |
165 | } | |
166 | ||
167 | // Also send standard error to the same log file. | |
168 | if (dup(1) < 0) | |
169 | { | |
170 | syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m"); | |
171 | return 1; | |
172 | } | |
173 | ||
174 | // Inform the io_service that we have finished becoming a daemon. The | |
175 | // io_service uses this opportunity to create any internal file descriptors | |
176 | // that need to be private to the new process. | |
177 | io_service.notify_fork(boost::asio::io_service::fork_child); | |
178 | ||
179 | // The io_service can now be used normally. | |
180 | syslog(LOG_INFO | LOG_USER, "Daemon started"); | |
181 | io_service.run(); | |
182 | syslog(LOG_INFO | LOG_USER, "Daemon stopped"); | |
183 | } | |
184 | catch (std::exception& e) | |
185 | { | |
186 | syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what()); | |
187 | std::cerr << "Exception: " << e.what() << std::endl; | |
188 | } | |
189 | } |