TFCweb  1.0.4 $Rev: 483 $
TFC Primavera 2012: Nucli d'un servidor web
tfcweb.cc
Veure la documentació d'aquest fitxer.
1 
10 /*
11  * Copyright (c) 2012 Toni Corvera
12  *
13  * This file is part of TFCWeb.
14  *
15  * TFCWeb is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * TFCWeb is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with TFCWeb. If not, see <http://www.gnu.org/licenses/>.
27  */
28 
29 #include "Configuracio.h"
30 #include "debug.h"
31 #include "Excepcions.h"
32 #include "Log.h"
33 #include "portabilitat.h"
34 #include "Recurs.h"
35 #include "Servidor.h"
36 #include "ThreadPool.h"
37 
38 #include <clocale>
39 #include <cstdlib>
40 #include <exception>
41 #include <iostream>
42 #include <functional>
43 #include <string>
44 #include <boost/asio.hpp>
45 #include <boost/assign.hpp>
46 #include <boost/filesystem.hpp>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/program_options.hpp>
49 #include <boost/thread.hpp>
50 #include <boost/unordered_map.hpp>
51 
52 using namespace boost;
53 using namespace boost::asio::ip;
54 using namespace boost::filesystem;
55 using namespace std;
56 using namespace tfc;
57 
58 namespace popt = boost::program_options;
59 
60 namespace {
61  const size_t BUFSIZE = 256;
62 
64 
65  // XXX: G++ 4.4 té problemes si la classe es defineix dins el cos de main()
66  struct Executor {
69  Executor(Configuracio & c, Servidor::socket_ptr & s) : cfg(c), sock(s) {}
70  void operator()() {
71  log() << NivellDebug
72  << "Servint petició en fil " << this_thread::get_id() << commit;
73  try {
74  // Salvaguarda: Intentem evitar sortir en casos no previstos
75  // A la pràctica no ho aconseguirem sempre perquè si es llença
76  // una excepció no declarada en la signatura s'abortarà
77  Servidor servidor(cfg, sock);
78  servidor.aten();
79  servidor.tancar_sessio();
80  }
81  catch (const std::exception & e) {
82  cerr << "Error no previst: " << e.what() << endl;
83  }
84  catch (...) {
85  cerr << "Error intern desconegut [" << FITXER_I_LINIA_ << "]" << endl;
86  }
87  log() << NivellDebug
88  << "(" << this_thread::get_id() << "): Sessió finalitzada" << commit;
89  }
90  };
91 } // ns anònim
92 
93 // Veure també http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/http/server/server.cpp
94 
95 void signal_callback_posix(int signum) {
96  static boost::unordered_map<int, string> noms_senyals = boost::assign::map_list_of
97  ( SIGINT, "SIGINT" )
98  ( SIGTERM, "SIGTERM" )
99 #ifdef SIGUSR1
100  ( SIGUSR1, "SIGUSR1" )
101 #endif
102  ;
103 
104  cerr << "> Sortida: senyal " << noms_senyals[signum] << " rebuda" << endl;
105  if (0 != pPool) {
106  pPool->termina();
107  }
108  ::exit(1);
109 }
110 
111 int main(int argc, char * argv[], char * envp[]) {
113  // 2: Petició d'interrupció, normalment per l'usuari (CTRL+C)
114  ::signal(SIGINT, signal_callback_posix);
115  // 15: Petició de finalització, p.e. `$ kill`.
116  // El senyal KILL (9) no es pot capturar
117  ::signal(SIGTERM, signal_callback_posix);
118 #ifdef SIGUSR1
119  ::signal(SIGUSR1, signal_callback_posix);
120 #endif
121 
122  // Interfície cap al sistema de xarxa de l'SO
123  asio::io_service io_service;
124 #if 0
125  // Gestió de senyals
126  asio::signal_set senyals(io_service, SIGINT, SIGTERM);
127 # ifdef SIGUSR1
128  senyals.add(SIGUSR1);
129 # endif
130  senyals.async_wait(signal_callback);
131  io_service.run();
132 #endif
133 
134  // Sortir en rebre senyal USR1
135  // FIXME: Portabilitat ::signal(SIGUSR1, signal_callback);
136 
137  cerr << "Servidor Web (TFC primavera 2012) v" VERSION "\n"
138  " Alumne: " AUTHOR " <" PACKAGE_BUGREPORT ">\n" << endl;
139 
140  try {
141  // Inicialització del logging
142  Log::singleton().associa(std::cerr, NIVELL_MES_VERBOS);
143  Log::singleton().associa(path("errors.log"), NivellError);
144 
145  Configuracio cfg(argc, argv);
146  // POST: Configuració carregada correctament
147  assert( filesystem::is_directory(cfg.arrel()) ); // INVARIANT
148  log() << cfg << commit;
149 
150  log() << "Iniciant thread pool..." << commit;
151  ThreadPool pool(cfg.fils());
152  pPool =& pool; // XXX: cal?
153 
154  log() << "> Obrint servei (port " << cfg.port() << ")..." << commit;
155  // FIXME: ipv6 ?? <- Millores futures?
156  tcp::acceptor acceptador(io_service, tcp::endpoint(tcp::v4(), cfg.port()));
157  // FIXME: throws: ??
158  log() << " + Escoltant en " << acceptador.local_endpoint() << commit;
159 
160  tfc::debug::init(pool);
161 
162  while (true) {
163  try { // Bloc try independent per a cada connexió
164  // TODO: Timeouts!
165  Servidor::socket_ptr psocket = Servidor::crea_socket(io_service);
166  log() << NivellInfo << "> Esperant connexió..." << commit;
167  acceptador.accept(*psocket);
168  //boost::asio::ip::tcp::socket so;
169  //cerr << "Sock: " << so.;
170  log() << NivellInfo << " + Connexió establerta "
171  << psocket->local_endpoint() << " <-> "
172  << psocket->remote_endpoint() << commit;
173 #ifdef THREADS_A_PETICIO
174  boost::thread(Executor(cfg, psocket));
175 #else
176  pool.programa(Executor(cfg, psocket));
177 #endif
178  }
179 #if 0 // Durant proves
180  catch (const boost::system::system_error & e) { // estén std::exception
181  cerr << "Error no previst (Boost): " << e.what() << endl;
182  }
183 #endif
184  catch (const std::exception & e) {
185  cerr << "Error no previst: " << e.what() << endl;
186  }
187  }
188  acceptador.cancel(); // XXX: Innecessari?
189  acceptador.close();
190  }
191  catch (const ErrorOpcions & e) {
192  cerr << e.what() << endl;
193  return e.codi();
194  }
195  catch (const AjudaDemanada & e) {
196  // Sortida correcta, s'ha demanat el missatge d'ajuda explícitament
197  cout << e.what() << endl;
198  return EX_OK;
199  }
200  catch (const VersioDemanada &) {
201  // Sortida correcta, s'ha demanat la informació de versions
202  cout << "Entorn de compilació:"
203  "\n Compilador : " TFC_COMPILADOR
204  "\n Sistema operatiu : " TFC_SO
205  "\n Arquitectura de CPU : " TFC_ARQUITECTURA
206  "\n Data de compilació : " __DATE__
207  "\n Biblioteques Boost : " TFC_BOOST
208  "\n zlib : " ZLIB_VERSION
209  << endl;
210  return EX_OK;
211  }
212  catch (const ErrorLogNoValid & e) { // No s'ha pogut obrir el log
213  cerr << "Error de log: " << e.what() << endl;
214  return EX_NOPERM;
215  }
216  catch (const std::exception & e) { // excepció base de la llibreria estàndard
217  cerr << "Error intern: " << e.what() << endl;
218  return EX_SOFTWARE;
219  }
220  catch (...) {
221  cerr << "Error intern desconegut [" << FITXER_I_LINIA_ << "]" << endl;
222  return EX_SOFTWARE;
223  }
224 
225  return EX_OK;
226 }
227 
228 // vim:set ts=4 et ai: //