]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/configuration/sgconfig/configproto.cpp
Better error reporting from plugin runner.
[stg.git] / projects / stargazer / plugins / configuration / sgconfig / configproto.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
19  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
20  */
21
22 #include "configproto.h"
23
24 #include "conn.h"
25
26 #include "parser_server_info.h"
27 #include "parser_admins.h"
28 #include "parser_tariffs.h"
29 #include "parser_users.h"
30 #include "parser_message.h"
31 #include "parser_user_info.h"
32 #include "parser_auth_by.h"
33
34 #include "stg/common.h"
35 #include "stg/logger.h"
36
37 #include <algorithm>
38 #include <functional>
39 #include <csignal>
40 #include <cstring>
41 #include <cerrno>
42 #include <cassert>
43
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48
49 namespace SP = STG::PARSER;
50
51 namespace
52 {
53
54 struct IsFinished : public std::unary_function<STG::Conn *, bool>
55 {
56     result_type operator()(const argument_type & arg)
57     {
58         return (arg->IsDone() && !arg->IsKeepAlive()) || !arg->IsOk();
59     }
60 };
61
62 struct RemoveConn : public std::unary_function<STG::Conn *, void>
63 {
64     result_type operator()(const argument_type & arg)
65     {
66         delete arg;
67     }
68 };
69
70 }
71
72 CONFIGPROTO::CONFIGPROTO(PLUGIN_LOGGER & l)
73     : m_settings(NULL),
74       m_admins(NULL),
75       m_tariffs(NULL),
76       m_users(NULL),
77       m_port(0),
78       m_running(false),
79       m_stopped(true),
80       m_logger(l),
81       m_listenSocket(-1)
82 {
83 }
84
85 CONFIGPROTO::~CONFIGPROTO()
86 {
87     std::for_each(m_conns.begin(), m_conns.end(), RemoveConn());
88 }
89
90 int CONFIGPROTO::Prepare()
91 {
92     sigset_t sigmask, oldmask;
93     sigemptyset(&sigmask);
94     sigaddset(&sigmask, SIGINT);
95     sigaddset(&sigmask, SIGTERM);
96     sigaddset(&sigmask, SIGUSR1);
97     sigaddset(&sigmask, SIGHUP);
98     pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
99
100     m_listenSocket = socket(PF_INET, SOCK_STREAM, 0);
101
102     if (m_listenSocket < 0)
103     {
104         m_errorStr = std::string("Cannot create listen socket: '") + strerror(errno) + "'.";
105         m_logger(m_errorStr);
106         return -1;
107     }
108
109     struct sockaddr_in listenAddr;
110     listenAddr.sin_family = PF_INET;
111     listenAddr.sin_port = htons(m_port);
112     listenAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // TODO: arbitrary address
113
114     int dummy = 1;
115
116     if (setsockopt(m_listenSocket, SOL_SOCKET, SO_REUSEADDR, &dummy, 4) != 0)
117     {
118         m_errorStr = std::string("Failed to set SO_REUSEADDR to the listen socket: '") + strerror(errno) + "'.";
119         m_logger(m_errorStr);
120         return -1;
121     }
122
123     if (bind(m_listenSocket, reinterpret_cast<sockaddr *>(&listenAddr), sizeof(listenAddr)) == -1)
124     {
125         m_errorStr = std::string("Cannot bind listen socket: '") + strerror(errno) + "'.";
126         m_logger(m_errorStr);
127         return -1;
128     }
129
130     if (listen(m_listenSocket, 64) == -1) // TODO: backlog length
131     {
132         m_errorStr = std::string("Failed to start listening for connections: '") + strerror(errno) + "'.";
133         m_logger(m_errorStr);
134         return -1;
135     }
136
137     RegisterParsers();
138
139     m_running = true;
140     m_stopped = false;
141     return 0;
142 }
143
144 int CONFIGPROTO::Stop()
145 {
146     m_running = false;
147     for (int i = 0; i < 5 && !m_stopped; ++i)
148     {
149         struct timespec ts = {0, 200000000};
150         nanosleep(&ts, NULL);
151     }
152
153     if (!m_stopped)
154     {
155         m_errorStr = "Cannot stop listenign thread.";
156         m_logger(m_errorStr);
157         return -1;
158     }
159
160     shutdown(m_listenSocket, SHUT_RDWR);
161     close(m_listenSocket);
162     return 0;
163 }
164
165 void CONFIGPROTO::Run()
166 {
167     while (m_running)
168     {
169         fd_set fds;
170
171         BuildFDSet(fds);
172
173         struct timeval tv;
174         tv.tv_sec = 0;
175         tv.tv_usec = 500000;
176
177         int res = select(MaxFD() + 1, &fds, NULL, NULL, &tv);
178         if (res < 0)
179         {
180             m_errorStr = std::string("'select' is failed: '") + strerror(errno) + "'.";
181             m_logger(m_errorStr);
182             break;
183         }
184         if (!m_running)
185             break;
186         if (res > 0)
187             HandleEvents(fds);
188
189         CleanupConns();
190     }
191     m_stopped = true;
192 }
193
194 void CONFIGPROTO::RegisterParsers()
195 {
196     assert(m_settings != NULL);
197     assert(m_store != NULL);
198     assert(m_admins != NULL);
199     assert(m_users != NULL);
200     assert(m_tariffs != NULL);
201
202     SP::GET_SERVER_INFO::FACTORY::Register(m_registry, *m_settings, *m_users, *m_tariffs);
203
204     SP::GET_ADMINS::FACTORY::Register(m_registry, *m_admins);
205     SP::ADD_ADMIN::FACTORY::Register(m_registry, *m_admins);
206     SP::DEL_ADMIN::FACTORY::Register(m_registry, *m_admins);
207     SP::CHG_ADMIN::FACTORY::Register(m_registry, *m_admins);
208
209     SP::GET_TARIFFS::FACTORY::Register(m_registry, *m_tariffs);
210     SP::ADD_TARIFF::FACTORY::Register(m_registry, *m_tariffs);
211     SP::DEL_TARIFF::FACTORY::Register(m_registry, *m_tariffs, *m_users);
212     SP::CHG_TARIFF::FACTORY::Register(m_registry, *m_tariffs);
213
214     SP::GET_USERS::FACTORY::Register(m_registry, *m_users);
215     SP::GET_USER::FACTORY::Register(m_registry, *m_users);
216     SP::ADD_USER::FACTORY::Register(m_registry, *m_users);
217     SP::DEL_USER::FACTORY::Register(m_registry, *m_users);
218     SP::CHG_USER::FACTORY::Register(m_registry, *m_users, *m_store, *m_tariffs);
219     SP::CHECK_USER::FACTORY::Register(m_registry, *m_users);
220
221     SP::SEND_MESSAGE::FACTORY::Register(m_registry, *m_users);
222
223     SP::AUTH_BY::FACTORY::Register(m_registry, *m_users);
224
225     SP::USER_INFO::FACTORY::Register(m_registry, *m_users);
226 }
227
228 int CONFIGPROTO::MaxFD() const
229 {
230     int maxFD = m_listenSocket;
231     for (size_t i = 0; i < m_conns.size(); ++i)
232         if (maxFD < m_conns[i]->Sock())
233             maxFD = m_conns[i]->Sock();
234     return maxFD;
235 }
236
237 void CONFIGPROTO::BuildFDSet(fd_set & fds) const
238 {
239     for (size_t i = 0; i < m_conns.size(); ++i)
240         FD_SET(m_conns[i]->Sock(), &fds);
241 }
242
243 void CONFIGPROTO::CleanupConns()
244 {
245     std::vector<STG::Conn *>::iterator pos;
246     pos = std::remove_if(m_conns.begin(), m_conns.end(), IsFinished());
247     if (pos == m_conns.end())
248         return;
249     std::for_each(pos, m_conns.end(), RemoveConn());
250     m_conns.erase(pos, m_conns.end());
251 }
252
253 void CONFIGPROTO::HandleEvents(const fd_set & fds)
254 {
255     if (FD_ISSET(m_listenSocket, &fds))
256         AcceptConnection();
257     else
258     {
259         for (size_t i = 0; i < m_conns.size(); ++i)
260             if (FD_ISSET(m_conns[i]->Sock(), &fds))
261                 m_conns[i]->Read();
262     }
263 }
264
265 void CONFIGPROTO::AcceptConnection()
266 {
267     struct sockaddr_in outerAddr;
268     socklen_t outerAddrLen(sizeof(outerAddr));
269     int sock = accept(m_listenSocket, reinterpret_cast<sockaddr *>(&outerAddr), &outerAddrLen);
270
271     if (sock < 0)
272     {
273         m_errorStr = std::string("Failed to accept connection: '") + strerror(errno) + "'.";
274         printfd(__FILE__, "%s", m_errorStr.c_str());
275         m_logger(m_errorStr);
276         return;
277     }
278
279     assert(m_admins != NULL);
280
281     try
282     {
283         m_conns.push_back(new STG::Conn(m_registry, *m_admins, sock, outerAddr));
284         printfd(__FILE__, "New connection from %s:%d\n", inet_ntostring(m_conns.back()->IP()).c_str(), m_conns.back()->Port());
285     }
286     catch (const STG::Conn::Error & error)
287     {
288         // Unlikely.
289         m_logger(std::string("Failed to create new client connection: '") + error.what() + "'.");
290     }
291 }
292 /*
293 void CONFIGPROTO::WriteLogAccessFailed(uint32_t ip)
294 {
295     m_logger("Admin's connection failed. IP %s", inet_ntostring(ip).c_str());
296 }
297 */