]> git.llucax.com Git - z.facultad/75.42/plaqui.git/blob - Server/src/server.cpp
Agrego mi intendo de documentacion con doxygen :-) ...
[z.facultad/75.42/plaqui.git] / Server / src / server.cpp
1 // vim: set noexpandtab tabstop=4 shiftwidth=4:
2 //----------------------------------------------------------------------------
3 //                                  PlaQui
4 //----------------------------------------------------------------------------
5 // This file is part of PlaQui.
6 //
7 // PlaQui is free software; you can redistribute it and/or modify it under the
8 // terms of the GNU General Public License as published by the Free Software
9 // Foundation; either version 2 of the License, or (at your option) any later
10 // version.
11 //
12 // PlaQui is distributed in the hope that it will be useful, but WITHOUT ANY
13 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 // details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with PlaQui; if not, write to the Free Software Foundation, Inc., 59 Temple
19 // Place, Suite 330, Boston, MA  02111-1307  USA
20 //----------------------------------------------------------------------------
21 // Creado:  Sat Oct 18 18:18:36 2003
22 // Autores: Leandro Lucarella <llucare@fi.uba.ar>
23 //----------------------------------------------------------------------------
24 //
25 // $Id$
26 //
27
28 #include "plaqui/server/server.h"
29 #include "plaqui/server/string.h"
30 #include "plaqui/server/connection.h"
31 #include "plaqui/server/controlserver.h"
32 #include <sigc++/class_slot.h>
33 #include <glibmm/timer.h>
34 #include <sstream>
35 #include <exception>
36 #ifdef DEBUG
37 #       include <iostream>
38 #endif // DEBUG
39
40 using namespace std;
41
42 namespace PlaQui {
43
44 namespace Server {
45
46 Server::~Server(void) {
47 #ifdef DEBUG
48         cerr << __FILE__ << "(" << __LINE__ << ")"
49                 <<  ": destructor." << endl;
50 #endif // DEBUG
51         // Mando a terminar todas las plantas.
52         plants_mutex.lock();
53         for (PlantList::iterator i = plants.begin(); i != plants.end(); i++) {
54                 i->second->finish();
55         }
56         PlantList::size_type count = plants.size();
57         plants_mutex.unlock();
58         // Espero que terminen realmente.
59         while (count) {
60                 Glib::usleep(10000); // 10 milisegundos
61                 plants_mutex.lock();
62                 count = plants.size();
63                 plants_mutex.unlock();
64         }
65 }
66
67 Server::Server(const Connection::Port& port)
68                 throw(sockerr): TCPServer(port) {
69 #ifdef DEBUG
70         cerr << __FILE__ << "(" << __LINE__ << ")"
71                 <<  ": port = " << port << endl;
72 #endif // DEBUG
73 }
74
75 bool Server::add_plant(const string& name, const string& filename) {
76         Glib::Mutex::Lock lock(plants_mutex);
77         if (plants.find(name) == plants.end()) { // No existe
78                 plants[name] = new Plant(filename);
79                 plants[name]->signal_finished().connect(SigC::bind(
80                                 SigC::slot_class(*this, &Server::on_plant_finished),
81                                 name.c_str()));
82                 plants[name]->run();
83                 return true;
84         }
85         return false;
86 }
87
88 Connection* Server::new_connection(const sockbuf::sockdesc& sd) {
89 #ifdef DEBUG
90         cerr << __FILE__ << "(" << __LINE__ << ")"
91                 <<  ": new_connection(sd = " << sd.sock << ")"
92                 << endl;
93 #endif // DEBUG
94         ControlServer* connection = new ControlServer(sd);
95         connection->signal_command_received().connect(SigC::bind(
96                         SigC::slot_class(*this, &Server::on_control_command_received),
97                         connection));
98         return connection;
99 }
100
101 void Server::on_plant_finished(const char* plant) {
102 #ifdef DEBUG
103         cerr << __FILE__ << "(" << __LINE__ << ")"
104                 <<  ": on_plant_finished(plant_name = " << plant << endl;
105 #endif // DEBUG
106         Glib::Mutex::Lock lock(plants_mutex);
107         plants.erase(plant);
108 }
109
110 void Server::on_control_command_received(const Command& command,
111                 ControlServer* controlserver) {
112 #ifdef DEBUG
113         cerr << __FILE__ << "(" << __LINE__ << ")"
114                 <<  ": on_control_command_received(target = "
115                 << command.get_target() << ", command = " << command.get_command()
116                 << ", args = [" << String::join(command.get_args(), ", ") << "])"
117                 << endl;
118 #endif // DEBUG
119         Response* response;
120         if (command.get_target() == "server") {
121                 if (command.get_command() == "info") {
122                         response = cmd_server_info();
123                 } else if (command.get_command() == "stop") {
124                         response = new Response(Response::OK,
125                                         "El server se cerrará en instantes");
126                         // XXX - Sin mandar la respuesta enseguida podría ser que el server
127                         // cierre la conexión antes de mandar la respuesta. En las pruebas
128                         // esto nunca pasó.
129                         //response->headers["Content-Type"] = "text/xml; charset=iso-8859-1";
130                         //controlserver->send(*response);
131                         //delete response;
132                         finish();
133                         //return;
134                 } else {
135                         response = new Response(Response::INVALID_COMMAND,
136                                         command.get_command() + " es un comando inválido para "
137                                         + "el destino 'server'");
138                 }
139         } else if (command.get_target() == "connection") {
140                 if (command.get_command() == "list") {
141                         response = cmd_connection_list();
142                 } else if (command.get_command() == "stop") {
143                         response = cmd_connection_stop(command);
144                 } else {
145                         response = new Response(Response::INVALID_COMMAND,
146                                         command.get_command() + " es un comando inválido para "
147                                         + "el destino 'connection'");
148                 }
149         } else if (command.get_target() == "transmission") {
150                 if (command.get_command() == "list") {
151                         response = cmd_transmission_list();
152                 } else if (command.get_command() == "start") {
153                         response = cmd_transmission_start(command);
154                 } else if (command.get_command() == "stop") {
155                         response = cmd_transmission_stop(command);
156                 } else {
157                         response = new Response(Response::INVALID_COMMAND,
158                                         command.get_command() + " es un comando inválido para "
159                                         + "el destino 'transmission'");
160                 }
161         } else if (command.get_target() == "plant") {
162                 if (command.get_command() == "list") {
163                         response = cmd_plant_list();
164                 } else if (command.get_command() == "get") {
165                         response = cmd_plant_get(command);
166                 } else if (command.get_command() == "set") {
167                         response = cmd_plant_set(command);
168                 } else if (command.get_command() == "set_frequency") {
169                         response = cmd_plant_set_frequency(command);
170                 } else if (command.get_command() == "start") {
171                         response = cmd_plant_start(command);
172                 } else if (command.get_command() == "stop") {
173                         response = cmd_plant_stop(command);
174                 } else if (command.get_command() == "remove") {
175                         response = cmd_plant_remove(command);
176                 } else {
177                         response = new Response(Response::INVALID_COMMAND,
178                                         command.get_command() + " es un comando inválido para "
179                                         + "el destino 'plant'");
180                 }
181         } else {
182                 response = new Response(Response::INVALID_TARGET, command.get_target()
183                                 + " es un destino inválido");
184         }
185         controlserver->send(*response);
186         delete response;
187 }
188
189 Response* Server::cmd_server_info(void) const {
190         stringstream xml;
191         xml << "<serverstatus>" << endl;
192         xml << "\t<version>" VERSION "</version>" << endl;
193         xml << "\t<authors>" << endl;
194         xml << "\t\t<author>Nicolás Dimov</author>" << endl;
195         xml << "\t\t<author>Leandro Lucarella</author>" << endl;
196         xml << "\t\t<author>Ricardo Markiewicz</author>" << endl;
197         xml << "\t</authors>" << endl;
198         xml << "</serverstatus>" << endl;
199         return new Response(xml.str());
200 }
201
202 Response* Server::cmd_connection_list(void) {
203         // TODO implementar con lista genérica.
204         TCPServer::ConnectionInfoList cil = get_connected();
205         stringstream xml;
206         xml << "<list type=\"connection\">" << endl;
207         for (TCPServer::ConnectionInfoList::const_iterator i = cil.begin();
208                         i != cil.end(); i++) {
209                 xml << "\t<row>" << endl;
210                 xml << "\t\t<host>" << i->host << "</host>" << endl;
211                 xml << "\t\t<port>" << i->port << "</port>" << endl;
212                 xml << "\t</row>" << endl;
213         }
214         xml << "</list>" << endl;
215         return new Response(xml.str());
216 }
217
218 Response* Server::cmd_connection_stop(const Command& command) {
219         const Command::Arguments& args = command.get_args();
220         Connection::Port port;
221         if (args.size() < 2) {
222                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
223                                 " para el comando 'stop' del destino 'connection'");
224         } else if (disconnect(args[0], to(args[1], port))) {
225                 return new Response(Response::OK, string("La conexión a ") + args[0]
226                                 + ":" + args[1] + " se cerrará en instantes");
227         } else {
228                 return new Response(Response::CONNECTION_NOT_FOUND,
229                                 string("No existe una conexión a ") + args[0] + ":" + args[1]);
230         }
231 }
232
233 Response* Server::cmd_transmission_list(void) {
234         // TODO implementar con lista genérica.
235         stringstream xml;
236         xml << "<list type=\"transmission\">" << endl;
237 /*FIXME plants_mutex.lock();
238         for (PlantList::const_iterator i = plants.begin();
239                         i != plants.end(); i++) {
240                 trans
241                 xml << "       <li>" << (*i)->get_host() << ":"
242                         << (*i)->get_port() << " [<a href=\"/transmission/stop/"
243                         << (*i)->get_host() << "/" << (*i)->get_port()
244                         << "\">desconectar</a>]</li>" << endl;
245         }
246         transmissions_mutex.unlock();*/
247         xml << "</list>" << endl;
248         return new Response(xml.str());
249 }
250
251 Response* Server::cmd_transmission_start(const Command& command) {
252         const Command::Arguments& args = command.get_args();
253         if (args.size() < 3) {
254                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
255                                 " para el comando 'start' del destino 'transmission'");
256         } else {
257                 string plant = args[0];
258                 string host = args[1];
259                 Connection::Port port = to(args[2], port);
260                 Glib::Mutex::Lock lock(plants_mutex);
261                 PlantList::iterator p = plants.find(plant);
262                 if (p == plants.end()) {
263                         return new Response(Response::PLANT_NOT_FOUND,
264                                         string("No existe la planta '") + plant + "'");
265                 // FIXME - agregar chequeo de que la transmision a ese host:port no exista.
266                 //         que use respuesta ALLREADY_EXISTS
267                 } else if (plants[plant]->transmission_start(host, port)) {
268                         return new Response(Response::OK,
269                                         string("Se empieza a transmitir la planta '") + plant
270                                         + "' a " + host + ":" + String().from(port));
271                 } else {
272                         return new Response(Response::ERROR_STARTING_TRANSMISSION,
273                                         string("Error al crear la transmisión a de la planta '")
274                                         + plant + "' a " + host + ":" + args[2]);
275                 }
276         }
277 }
278
279 Response* Server::cmd_transmission_stop(const Command& command) {
280         const Command::Arguments& args = command.get_args();
281         if (args.size() < 2) {
282                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
283                                 " para el comando 'stop' del destino 'transmission'");
284         } else {
285                 const string& host = args[0];
286                 Connection::Port port = to(args[1], port);
287                 for (PlantList::iterator i = plants.begin(); i != plants.end(); i++) {
288                         // TODO - agregar chequeo para saber si existe una conexion (para
289                         // tirar error de que no hay conexion o de que no se pudo
290                         // desconectar.
291                         if (i->second->transmission_stop(host, port)) {
292                                 return new Response(Response::OK,
293                                                 string("La transmisión de la planta '") + i->first
294                                                         + "' a " + host + ":" + args[1]
295                                                         + " se cerrará en instantes");
296                         }
297                 }
298                 return new Response(Response::TRANSMISSION_NOT_FOUND,
299                                 string("No existe una transmisión a ") + host + ":" + args[1]);
300         }
301 }
302
303 Response* Server::cmd_plant_list(void) {
304         // FIXME hacer con ResponseList
305         stringstream xml;
306         xml << "<list type=\"plant\">" << endl;
307         plants_mutex.lock();
308         for (PlantList::const_iterator i = plants.begin();
309                         i != plants.end(); i++) {
310                 xml << "\t<row>" << endl;
311                 xml << "\t\t<name>" << i->first << "</name>" << endl;
312                 xml << "\t</row>" << endl;
313         }
314         plants_mutex.unlock();
315         xml << "</list>" << endl;
316         return new Response(xml.str());
317 }
318
319 Response* Server::cmd_plant_get(const Command& command) {
320         if (!command.get_args().size()) {
321                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
322                                 " para el comando 'get' del destino 'plant'");
323         }
324         Glib::Mutex::Lock lock(plants_mutex);
325         string plant = command.get_args()[0];
326         if (plants.find(plant) == plants.end()) {
327                 return new Response(Response::PLANT_NOT_FOUND,
328                                 string("No existe la planta '") + plant + "'");
329         }
330         string xml = plants[plant]->get_xml();
331         if (xml.length()) {
332                 return new Response(xml);
333         } else {
334                 return new Response(Response::ERROR_GETING_PLANT_XML,
335                                 ("No se pudo obtener el XML de la planta '") + plant + "'");
336         }
337 }
338
339 Response* Server::cmd_plant_set(const Command& command) {
340         const Command::Arguments& args = command.get_args();
341         if (args.size() < 4) {
342                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
343                                 " para el comando 'set' del destino 'plant'");
344         }
345         string plant = args[0];
346         string element = args[1];
347         string input = args[2];
348         if (input != "open") {
349                 return new Response(Response::ELEMENT_INPUT_NOT_FOUND,
350                                 string("El elemento '") + element + "' de la planta '" + plant
351                                 + "' no tiene una entrada '" + input + "'");
352         }
353         string value = args[3];
354         Glib::Mutex::Lock lock(plants_mutex);
355         PlantList::iterator p = plants.find(plant);
356         if (p == plants.end()) {
357                 return new Response(Response::PLANT_NOT_FOUND,
358                                 string("No existe la planta '") + plant + "'");
359         }
360         bool open = true;
361         if ((value == "false") || (value == "0") || (value == "off")
362                         || (value == "no")) {
363                 open = false;
364         }
365         if (!plants[plant]->set_open(element, open)) {
366                 return new Response(Response::ERROR_CHANGING_ELEMENT_INPUT,
367                                 string("No se pudo cambiar la entrada '") + input
368                                 + "' del elemento '" + element + "' de la planta '"
369                                 + plant + "'");
370         }
371         return new Response(Response::OK,
372                         string("Se cambió la entrada '") + input + "' del elemento '"
373                         + element + "' de la planta '" + plant + "' a '" + value + "'");
374 }
375
376 Response* Server::cmd_plant_set_frequency(const Command& command) {
377         if (command.get_args().size() < 2) {
378                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
379                                 " para el comando 'set_frequency' del destino 'plant'");
380         }
381         Glib::Mutex::Lock lock(plants_mutex);
382         const string name = command.get_args()[0];
383         if (plants.find(name) == plants.end()) {
384                 return new Response(Response::PLANT_NOT_FOUND,
385                                 string("No existe la planta '") + name + "'");
386         }
387         unsigned hz;
388         to(command.get_args()[1], hz);
389         plants[name]->set_frequency(hz);
390         String shz;
391         shz.from(hz);
392         return new Response(Response::OK,
393                         string("Se cambió la frecuencia de refresco de la planta '") + name
394                         + "' a '" + shz + "' veces por segundo");
395 }
396
397 Response* Server::cmd_plant_start(const Command& command) {
398         if (!command.get_args().size()) {
399                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
400                                 " para el comando 'start' del destino 'plant'");
401         }
402         Glib::Mutex::Lock lock(plants_mutex);
403         const string name = command.get_args()[0];
404         if (plants.find(name) == plants.end()) {
405                 return new Response(Response::PLANT_NOT_FOUND,
406                                 string("No existe la planta '") + name + "'");
407         }
408         plants[name]->set_paused(false);
409         return new Response(Response::OK,
410                         string("La planta '") + name + "' fue reanudada");
411 }
412
413 Response* Server::cmd_plant_stop(const Command& command) {
414         if (!command.get_args().size()) {
415                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
416                                 " para el comando 'stop' del destino 'plant'");
417         }
418         Glib::Mutex::Lock lock(plants_mutex);
419         const string name = command.get_args()[0];
420         if (plants.find(name) == plants.end()) {
421                 return new Response(Response::PLANT_NOT_FOUND,
422                                 string("No existe la planta '") + name + "'");
423         }
424         plants[name]->set_paused(true);
425         return new Response(Response::OK,
426                         string("La planta '") + name + "' fue pausada");
427 }
428
429 Response* Server::cmd_plant_remove(const Command& command) {
430         if (!command.get_args().size()) {
431                 return new Response(Response::ARGUMENT_MISSING, "Faltan argumentos "
432                                 " para el comando 'remove' del destino 'plant'");
433         }
434         Glib::Mutex::Lock lock(plants_mutex);
435         const string name = command.get_args()[0];
436         if (plants.find(name) == plants.end()) {
437                 return new Response(Response::PLANT_NOT_FOUND,
438                                 string("No existe la planta '") + name + "'");
439         }
440         plants[name]->finish();
441         return new Response(Response::OK,
442                         string("La planta '") + name + "' fue será removida del servidor "
443                         "en instantes");
444 }
445
446 } // namespace Server
447
448 } // namespace PlaQui
449