Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

direct/src/directd/directd.cxx

Go to the documentation of this file.
00001 // Filename: directd.cxx
00002 // Created by:  skyler 2002.04.08
00003 // Based on test_tcp_*.* by drose.
00004 //
00005 ////////////////////////////////////////////////////////////////////
00006 //
00007 // PANDA 3D SOFTWARE
00008 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00009 //
00010 // All use of this software is subject to the terms of the Panda 3d
00011 // Software license.  You should have received a copy of this license
00012 // along with this source code; you will also find a current copy of
00013 // the license at http://www.panda3d.org/license.txt .
00014 //
00015 // To contact the maintainers of this program write to
00016 // panda3d@yahoogroups.com .
00017 //
00018 ////////////////////////////////////////////////////////////////////
00019 
00020 // This define tells the windows headers to include job objects:
00021 #define _WIN32_WINNT 0x0500
00022 
00023 #include "directd.h"
00024 /*#include "pandaFramework.h"
00025 #include "queuedConnectionManager.h"*/
00026 
00027 //#include <process.h>
00028 //#include <Windows.h>
00029 //#include "pandabase.h"
00030 
00031 //#include "queuedConnectionManager.h"
00032 //#include "queuedConnectionListener.h"
00033 //#include "queuedConnectionReader.h"
00034 //#include "connectionWriter.h"
00035 #include "netAddress.h"
00036 #include "connection.h"
00037 #include "datagramIterator.h"
00038 #include "netDatagram.h"
00039 
00040 #include "pset.h"
00041 
00042 namespace {
00043   // ...This section is part of the old stuff from the original implementation.
00044   // The new stuff that uses job objects doesn't need this stuff:
00045 
00046   // The following is from an MSDN example:
00047   
00048   #define TA_FAILED 0
00049   #define TA_SUCCESS_CLEAN 1
00050   #define TA_SUCCESS_KILL 2
00051   #define TA_SUCCESS_16 3
00052 
00053   BOOL CALLBACK
00054   TerminateAppEnum(HWND hwnd, LPARAM lParam) {
00055     DWORD dwID;
00056     GetWindowThreadProcessId(hwnd, &dwID);
00057     if(dwID == (DWORD)lParam) {
00058        PostMessage(hwnd, WM_CLOSE, 0, 0);
00059     }
00060     return TRUE;
00061   }
00062 
00063   /*
00064       DWORD WINAPI TerminateApp(DWORD dwPID, DWORD dwTimeout)
00065 
00066       Purpose:
00067         Shut down a 32-Bit Process
00068 
00069       Parameters:
00070         dwPID
00071            Process ID of the process to shut down.
00072 
00073         dwTimeout
00074            Wait time in milliseconds before shutting down the process.
00075 
00076       Return Value:
00077         TA_FAILED - If the shutdown failed.
00078         TA_SUCCESS_CLEAN - If the process was shutdown using WM_CLOSE.
00079         TA_SUCCESS_KILL - if the process was shut down with
00080            TerminateProcess().
00081   */ 
00082   DWORD WINAPI
00083   TerminateApp(DWORD dwPID, DWORD dwTimeout) {
00084     HANDLE   hProc;
00085     DWORD   dwRet;
00086 
00087     // If we can't open the process with PROCESS_TERMINATE rights,
00088     // then we give up immediately.
00089     hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID);
00090     if(hProc == NULL) {
00091        return TA_FAILED;
00092     }
00093 
00094     // TerminateAppEnum() posts WM_CLOSE to all windows whose PID
00095     // matches your process's.
00096     EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)dwPID);
00097 
00098     // Wait on the handle. If it signals, great. If it times out,
00099     // then you kill it.
00100     if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0) {
00101        dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
00102     } else {
00103        dwRet = TA_SUCCESS_CLEAN;
00104     }
00105     CloseHandle(hProc);
00106 
00107     return dwRet;
00108   }
00109 
00110   /*
00111       Start an application with the command line cmd.
00112       returns the process id of the new process/application.
00113   */
00114   DWORD
00115   StartApp(const string& cmd) {
00116     DWORD pid=0;
00117     STARTUPINFO si; 
00118     PROCESS_INFORMATION pi; 
00119     ZeroMemory(&si, sizeof(STARTUPINFO));
00120     si.cb = sizeof(STARTUPINFO); 
00121     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
00122     if (CreateProcess(NULL, (char*)cmd.c_str(), 
00123         0, 0, 1, NORMAL_PRIORITY_CLASS, 
00124         0, 0, &si, &pi)) {
00125       pid=pi.dwProcessId;
00126       CloseHandle(pi.hProcess);
00127       CloseHandle(pi.hThread);
00128     } else {
00129       nout<<"CreateProcess failed: "<<cmd<<endl;
00130     }
00131     return pid;
00132   }
00133 
00134 }
00135 
00136 DirectD::DirectD() :
00137     _reader(&_cm, 1), _writer(&_cm, 0), _listener(&_cm, 0),
00138     _jobObject(0), _shutdown(false), _useOldStuff(false) {
00139 }
00140 
00141 DirectD::~DirectD() {
00142   // Close all the connections:
00143   ConnectionSet::iterator ci;
00144   for (ci = _connections.begin(); ci != _connections.end(); ++ci) {
00145     _cm.close_connection((*ci));
00146   }
00147   _connections.clear();
00148   
00149   kill_all();
00150 }
00151 
00152 int 
00153 DirectD::client_ready(const string& server_host, int port,
00154     const string& cmd) {
00155   stringstream ss;
00156   ss<<"!"<<cmd;
00157   send_one_message(server_host, port, ss.str());
00158   return 0;
00159 }
00160 
00161 int 
00162 DirectD::tell_server(const string& server_host, int port,
00163     const string& cmd) {
00164   send_one_message(server_host, port, cmd);
00165   return 0;
00166 }
00167 
00168 bool
00169 DirectD::wait_for_servers(int count, int timeout_ms) {
00170   if (count <= 0) {
00171     return true;
00172   }
00173   // The timeout is a rough estimate, we may wait slightly longer.
00174   const wait_ms=200;
00175   int cycles=timeout_ms/wait_ms;
00176   while (cycles--) {
00177     check_for_new_clients();
00178     check_for_lost_connection();
00179     /*
00180         The following can be more generalized with a little work.
00181         check_for_datagrams() could take a handler function as an arugment, maybe.
00182     */
00183     ////check_for_datagrams();
00184     // Process all available datagrams.
00185     while (_reader.data_available()) {
00186       NetDatagram datagram;
00187       if (_reader.get_data(datagram)) {
00188         cout << count << ": Server at " << datagram.get_address()
00189             << " is ready." << endl;
00190         datagram.dump_hex(nout);
00191         //handle_datagram(datagram);
00192         DatagramIterator di(datagram);
00193         string s=di.get_string();
00194         if (s=="r" && !--count) {
00195           return true;
00196         }
00197       }
00198     }
00199 
00200     // Yield the timeslice before we poll again.
00201     PR_Sleep(PR_MillisecondsToInterval(wait_ms));
00202   }
00203   // We've waited long enough, assume they're not going to be
00204   // ready in the time we want them:
00205   return false;
00206 }
00207 
00208 int 
00209 DirectD::server_ready(const string& client_host, int port) {
00210   send_one_message(client_host, port, "r");
00211   return 0;
00212 }
00213 
00214 
00215 void
00216 DirectD::start_app(const string& cmd) {
00217   nout<<"start_app(cmd="<<cmd<<")"<<endl;
00218   if (_useOldStuff) {
00219     _pids.push_back(StartApp(cmd));
00220     nout<<"    pid="<<_pids.back()<<endl;
00221   } else {
00222     if (!_jobObject) {
00223       _jobObject=CreateJobObject(0, 0);
00224       if (!_jobObject) {
00225         nout<<"CreateProcess failed: no _jobObject: "<<GetLastError()<<endl;
00226         return;
00227       }
00228     }
00229     DWORD pid=0;
00230     STARTUPINFO si; 
00231     PROCESS_INFORMATION pi; 
00232     ZeroMemory(&si, sizeof(STARTUPINFO));
00233     si.cb = sizeof(STARTUPINFO); 
00234     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
00235     if (CreateProcess(NULL, (char*)cmd.c_str(), 
00236         0, 0, 1, NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED, 
00237         0, 0, &si, &pi)) {
00238       // The process must be created with CREATE_SUSPENDED to
00239       // give us a chance to get the handle into our sgJobObject
00240       // before the child processes starts sub-processes.
00241       if (!AssignProcessToJobObject(_jobObject, pi.hProcess)) {
00242         // ...The assign failed.
00243         cerr<<"StartJob AssignProcessToJobObject Error: "<<GetLastError()<<endl;
00244       }
00245       CloseHandle(pi.hProcess); //?????
00246       // Because we called CreateProcess with the CREATE_SUSPEND flag,
00247       // we must explicitly resume the processes main thread.
00248       if (ResumeThread(pi.hThread) == -1) {
00249         cerr<<"StartJob ResumeThread Error: "<<GetLastError()<<endl;
00250       }
00251       CloseHandle(pi.hThread);
00252     } else {
00253       nout<<"StartJob CreateProcess failed: "<<cmd<<endl;
00254     }
00255   }
00256 }
00257 
00258 void
00259 DirectD::kill_app(int index) {
00260   if (_useOldStuff) {
00261     int i = _pids.size() - 1 - index % _pids.size();
00262     PidStack::iterator pi = _pids.begin() + i;
00263     if (pi!=_pids.end()) {
00264       nout<<"trying kill "<<(*pi)<<endl;
00265       TerminateApp((*pi), 1000);
00266       _pids.erase(pi);
00267     }
00268   } else {
00269     cerr<<"kill_app(index) not implemented, calling kill_all() instead."<<endl;
00270     kill_all();
00271   }
00272 }
00273 
00274 void
00275 DirectD::kill_all() {
00276   if (_useOldStuff) {
00277     PidStack::reverse_iterator pi;
00278     for (pi = _pids.rbegin(); pi != _pids.rend(); ++pi) {
00279       nout<<"trying kill "<<(*pi)<<endl;
00280       TerminateApp((*pi), 1000);
00281     }
00282     _pids.clear();
00283   } else {
00284     if (!_jobObject) {
00285       cerr<<"kill_all(): No open _jobObject"<<endl;
00286     } else if (!TerminateJobObject(_jobObject, 0)) {
00287       cerr<<"kill_all() TerminateJobObject Error: "<<GetLastError()<<endl;
00288     }
00289     CloseHandle(_jobObject);
00290     _jobObject=0;
00291   }
00292 }
00293 
00294 void
00295 DirectD::send_command(const string& cmd) {
00296   NetDatagram datagram;
00297   datagram.add_string(cmd);
00298   // Send the datagram.
00299   ConnectionSet::iterator ci;
00300   for (ci = _connections.begin(); ci != _connections.end(); ++ci) {
00301     _writer.send(datagram, (*ci));
00302   }
00303 }
00304 
00305 void
00306 DirectD::handle_datagram(NetDatagram& datagram){
00307   DatagramIterator di(datagram);
00308   string cmd=di.get_string();
00309   handle_command(cmd);
00310 }
00311 
00312 void
00313 DirectD::handle_command(const string& cmd) {
00314   nout<<"DirectD::handle_command: "<<cmd<<endl;
00315 }
00316 
00317 void
00318 DirectD::send_one_message(const string& host_name, 
00319     int port,
00320     const string& message) {
00321   NetAddress host;
00322   if (!host.set_host(host_name, port)) {
00323     nout << "Unknown host: " << host_name << "\n";
00324   }
00325 
00326   const int timeout_ms=5000;
00327   PT(Connection) c = _cm.open_TCP_client_connection(host, timeout_ms);
00328   if (c.is_null()) {
00329     nout << "No connection.\n";
00330     return;
00331   }
00332 
00333   nout << "Successfully opened TCP connection to " << host_name
00334        << " on port "
00335        << c->get_address().get_port() << " and IP "
00336        << c->get_address() << "\n";
00337 
00338   //_reader.add_connection(c);
00339   
00340   NetDatagram datagram;
00341   datagram.add_string(message);
00342   _writer.send(datagram, c);
00343   
00344   //PR_Sleep(PR_MillisecondsToInterval(200));
00345   //wait_for_servers(1, 10*1000);
00346   //_reader.remove_connection(c);
00347   _cm.close_connection(c);
00348 }
00349 
00350 int
00351 DirectD::connect_to(const string& host_name, int port) {
00352   NetAddress host;
00353   if (!host.set_host(host_name, port)) {
00354     nout << "Unknown host: " << host_name << "\n";
00355   }
00356 
00357   const int timeout_ms=5000;
00358   PT(Connection) c = _cm.open_TCP_client_connection(host, timeout_ms);
00359   if (c.is_null()) {
00360     nout << "No connection.\n";
00361     return 0;
00362   }
00363 
00364   nout << "Successfully opened TCP connection to " << host_name
00365        << " on port "
00366        << c->get_address().get_port() << " and IP "
00367        << c->get_address() << "\n";
00368 
00369   _reader.add_connection(c);
00370   _connections.insert(c);
00371   return  c->get_address().get_port();
00372 }
00373 
00374 void
00375 DirectD::disconnect_from(const string& host_name, int port) {
00376   nout<<"disconnect_from(\""<<host_name<<", port="<<port<<")"<<endl;
00377   for (ConnectionSet::iterator i=_connections.begin(); i != _connections.end(); ++i) {
00378     nout<<"    found "<<(*i)->get_address().get_ip_string()<<", port "<<(*i)->get_address().get_port()<<endl;
00379     if ((*i)->get_address().get_ip_string()==host_name) {
00380       nout<<"    disconnecting."<<endl;
00381       _reader.remove_connection((*i));
00382       _cm.close_connection((*i));
00383       _connections.erase(i);
00384       break;
00385     }
00386   }
00387 }
00388 
00389 void
00390 DirectD::check_for_lost_connection() {
00391   while (_cm.reset_connection_available()) {
00392     PT(Connection) c;
00393     if (_cm.get_reset_connection(c)) {
00394       nout<<"Lost connection from "<<c->get_address()<<endl;
00395       _connections.erase(c);
00396       _cm.close_connection(c);
00397     }
00398   }
00399 }
00400 
00401 void
00402 DirectD::check_for_datagrams(){
00403   // Process all available datagrams.
00404   while (_reader.data_available()) {
00405     NetDatagram datagram;
00406     if (_reader.get_data(datagram)) {
00407       nout << "Got datagram " /*<< datagram <<*/ "from "
00408            << datagram.get_address() << endl;
00409       datagram.dump_hex(nout);
00410       handle_datagram(datagram);
00411     }
00412   }
00413 }
00414 
00415 void
00416 DirectD::listen_to(int port, int backlog) {
00417   PT(Connection) rendezvous = _cm.open_TCP_server_rendezvous(port, backlog);
00418   if (rendezvous.is_null()) {
00419     nout << "Cannot grab port " << port << ".\n";
00420     exit(1);
00421   }
00422   nout << "Listening for connections on port " << port << "\n";
00423   _listener.add_connection(rendezvous);
00424 }
00425 
00426 void
00427 DirectD::check_for_new_clients() {
00428   while (_listener.new_connection_available()) {
00429     PT(Connection) rv;
00430     NetAddress address;
00431     PT(Connection) new_connection;
00432     if (_listener.get_new_connection(rv, address, new_connection)) {
00433       nout << "Got connection from " << address << "\n";
00434       _reader.add_connection(new_connection);
00435       _connections.insert(new_connection);
00436     }
00437   }
00438 }

Generated on Fri May 2 01:37:20 2003 for Direct by doxygen1.3