OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimToolServer.cpp
Go to the documentation of this file.
1 //**************************************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //**************************************************************************************************
7 
9 #include <iostream>
10 #include <sstream>
11 #include <map>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <ossim/init/ossimInit.h>
21 #include <ossim/base/ossimString.h>
24 #include <ossim/base/ossimNotify.h>
28 
29 #ifdef _MSC_VER
30 #include <winsock2.h>
31 #include <process.h> /* for getpid() and the exec..() family */
32 #include <io.h>
33 #define dup2 _dup2
34 #define close closesocket
35 #define pipe(phandles) _pipe(phandles, 4096, _O_BINARY)
36 #else
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <arpa/inet.h>
41 #include <netdb.h>
42 #include <netinet/tcp.h>
43 #include <netinet/in.h>
44 #endif
45 
46 
47 #define OWARN ossimNotify(ossimNotifyLevel_WARN)
48 #define OINFO ossimNotify(ossimNotifyLevel_INFO)
49 #define MAX_BUF_LEN 4096
50 #define FORK_PROCESS false
51 #define _DEBUG_ false
52 
54 : m_svrsockfd(-1),
55  m_clisockfd(-1),
56  m_buffer(new char[MAX_BUF_LEN])
57 {}
58 
60 {
61  close(m_svrsockfd);
62 }
63 
64 
65 void ossimToolServer::startListening(const char* portid)
66 {
67  initSocket(portid);
68 
69  socklen_t clilen;
70  ostringstream xmsg;
71  bool status_ok = true;
72 
73  // Loop forever to listen for OSSIM requests:
74  OINFO<<"Waiting for connections...\n"<<endl;
75  while (1)
76  {
77  // Message received at server socket, establish connection to client's socket:
78  struct sockaddr_in cli_addr;
79  clilen = sizeof(cli_addr);
80  m_clisockfd = accept(m_svrsockfd, (struct sockaddr *) &cli_addr, &clilen);
81  if (m_clisockfd < 0)
82  error("Error accepting message on port.");
83 
84  // Test code:
85  char clientname[256];
86  char clientport[256];
87  getnameinfo((struct sockaddr *) &cli_addr, clilen, clientname, 256, clientport, 256, 0);
88  cout<<"ossimToolServer: Got connection from "<<clientname<<":"<<clientport
89  <<" Forking child process..."<<endl;
90 
91 
92 #if 1
93  // Fork process to handle client request:
94  if (FORK_PROCESS)
95  {
96  if (!fork())
97  {
98  // this is the child process
99  close(m_svrsockfd); // child doesn't need the listener
100 
101  // Receive request from client:
102  bool connected = true;
103  while (connected)
104  connected = processOssimRequest(cli_addr);
105 
106  exit(0); // exit forked process
107  }
108  }
109  else
110  {
111  bool connected = true;
112  while (connected)
113  connected = processOssimRequest(cli_addr);
114  }
115 #else
116  int n=0;
117  while (1)
118  {
119  // Clear the input m_buffer and read the message sent:
120  memset(m_buffer, 0, 256);
121  n = recv(m_clisockfd,m_buffer,256,0);
122  if (n < 0)
123  {
124  printf("recv returned -1 from port %d.\n", m_clisockfd);
125  }
126 
127  // Send acknowledgement back to client:
128  printf("Received: %s\n",m_buffer);
129  string msg ="server received message: ";
130  msg.append(m_buffer, strlen(m_buffer));
131  n = send(m_clisockfd, msg.c_str(), msg.size(), 0);
132  if (n < 0)
133  error("ERROR writing to socket");
134  }
135 #endif
136  // Finished serving this client. Close the connection:
137  close(m_clisockfd);
138  }
139 }
140 
141 void ossimToolServer::initSocket(const char* portid)
142 {
143  // Establish full server address including port:
144  struct addrinfo hints;
145  memset(&hints, 0, sizeof hints); // make sure the struct is empty
146  hints.ai_family = AF_INET; // don't care IPv4 or IPv6
147  hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
148  hints.ai_flags = AI_PASSIVE; // fill in my IP for me
149  struct addrinfo *res;
150 
151  int failed = getaddrinfo(NULL, portid, &hints, &res);
152  if (failed)
153  error(gai_strerror(failed));
154 
155  // Create socket for this server by walking the linked list of available addresses:
156  struct addrinfo *server_info = res;
157  while (server_info)
158  {
159  m_svrsockfd = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol);
160  if (m_svrsockfd >= 0)
161  break;
162  server_info = server_info->ai_next;
163  }
164  if ((m_svrsockfd < 0) || (server_info == NULL))
165  error("Error opening socket");
166 
167  // lose the pesky "Address already in use" error message when requently restarting the daemon:
168  int yes=1;
169  if (setsockopt(m_svrsockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1)
170  error("Error on setsockopt() call");
171 
172 #if defined (__APPLE__) || defined(__FreeBSD__)
173  int bindResult = ::bind(m_svrsockfd, server_info->ai_addr, server_info->ai_addrlen);
174 #else
175  int bindResult = bind(m_svrsockfd, server_info->ai_addr, server_info->ai_addrlen);
176 #endif
177  // Bind the server's socket to the specified port number:
178  if ( bindResult < 0)
179  error("Error on binding to socket:port.");
180 
181  struct sockaddr_in *server_addr = (sockaddr_in*) &(server_info->ai_addr);
182 
183  OINFO<<"ossimToolServer daemon started. Listening on port "<<portid<<". Process ID: "<<getpid()<<"\n"<<endl;
184  freeaddrinfo(server_info);
185 
186  // Start listening:
187  if (listen(m_svrsockfd, 5) == -1)
188  error("Error on listen()");
189 
190  // Reap all dead processes:
191  struct sigaction sa;
192  sa.sa_handler = ossimToolServer::sigchld_handler;
193  sigemptyset(&sa.sa_mask);
194  sa.sa_flags = SA_RESTART;
195  if (sigaction(SIGCHLD, &sa, NULL) == -1)
196  error("Error on sigaction()");
197 }
198 
200 {
201  // waitpid() might overwrite errno, so we save and restore it:
202  int saved_errno = errno;
203  while(waitpid(-1, NULL, WNOHANG) > 0);
204  errno = saved_errno;
205 }
206 
207 void ossimToolServer::error(const char* msg)
208 {
209  perror(msg);
210  exit (1);
211 }
212 
213 void ossimToolServer::writeSocket(const char* buf, int bufsize)
214 {
215  int remaining = bufsize;
216  int n;
217 
218  //int optval = 1;
219  //setsockopt(clientfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
220 
221  while (remaining)
222  {
223  n = send(m_clisockfd, buf, remaining, 0);
224  if (n < 0)
225  error("ERROR writing to socket");
226  remaining -= n;
227  }
228 }
229 
231 {
232  ostringstream xmsg;
233 
234  // Open the server-side image file:
235  ifstream svrfile (fname.chars(), ios::binary|ios::in);
236  if (svrfile.fail())
237  {
238  xmsg<<"ossimToolServer.sendFile() -- Error opening file <"<<fname<<">."<<endl;
239  error(xmsg.str().c_str());
240  }
241 
242  // Determine file size:
243  std::streampos fsize = svrfile.tellg();
244  svrfile.seekg( 0, std::ios::end );
245  fsize = svrfile.tellg() -fsize;
246  svrfile.seekg( 0, std::ios::beg );
247 
248  // Send file size to the client:
249  char size_response[19];
250  sprintf(size_response, "SIZE: %012d", (int) fsize);
251  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" sending <"<<size_response<<">"<<endl; //TODO REMOVE DEBUG
252  writeSocket(size_response, strlen(size_response));
253  if (!acknowledgeRcvd())
254  return false;
255 
256  // Send file name to the client:
257  char name_response[256];
258  memset(name_response, 0, 256);
259  sprintf(name_response, "NAME: %s", fname.file().chars());
260  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" sending <"<<name_response<<">"<<endl; //TODO REMOVE DEBUG
261  writeSocket(name_response, strlen(name_response));
262  if (!acknowledgeRcvd())
263  return false;
264 
265  memset(m_buffer, 0, MAX_BUF_LEN);
266 
267  // Send image in MAX_BUF_LEN byte packets
268  int n = 0;
269  int r = 0;
270  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" sending binary data..."<<endl; //TODO REMOVE DEBUG
271  while (!svrfile.eof())
272  {
273  // Read server-side file block:
274  svrfile.read(m_buffer, MAX_BUF_LEN);
275  if (svrfile.bad())
276  error("ossimToolServer.sendFile() -- Error during file read()");
277 
278  n = svrfile.gcount();
279  r += n;
280 
281  // transmit the block to the client:
283  }
284  if (!acknowledgeRcvd())
285  return false;
286 
287  cout << "Send complete."<<endl;
288  svrfile.close();
289  return true;
290 }
292 {
293  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" Waiting to recv"<<endl; //TODO REMOVE DEBUG
294  int n = recv(m_clisockfd, m_buffer, 11, 0);
295  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" Received <"<<m_buffer<<">"<<endl; //TODO REMOVE DEBUG
296  if (n < 0)
297  error("ossimToolServer: EOF encountered reading from port");
298  if (strcmp(m_buffer, "ok_to_send"))
299  return false;
300  if (_DEBUG_) cout << "Send acknowledged by client."<<endl;
301  return true;
302 }
303 
305 {
306  ostringstream xmsg;
307  bool status_ok = false;
308  static const char* msg = "\nossimToolServer.runCommand(): ";
309 
310  // Intercept test mode:
311  if (command == "sendfile")
312  {
313  ossimFilename fname = command.after("sendfile").trim();
314  const char* response = "FILE ";
315  writeSocket(response, strlen(response));
316  sendFile(fname);
317  return true;
318  }
319 
320  // Redirect stdout:
321  memset(m_buffer, 0, MAX_BUF_LEN);
322  int pipeDesc[2] = {0,0};
323  int savedStdout = dup( fileno(stdout) );
324  if( pipe( pipeDesc ) == -1 )
325  error("Could not redirect stdout (1).");
326  setbuf( stdout, NULL );
327  dup2( pipeDesc[1], fileno(stdout) );
328 
329 #ifdef _MSC_VER
330  u_long iMode = 1;
331  ioctlsocket(pipeDesc[0], FIONBIO, &iMode);
332 #else
333  fcntl( pipeDesc[0], F_SETFL, O_NONBLOCK );
334 #endif
335 
337  ossimRefPtr<ossimTool> utility = 0;
338 
339  // Intercept help request:
340  ossimString c1 = command.before(" ");
341  ossimString c2 = command.after(" ");
342  while (1)
343  {
344  if (c1 == "help")
345  {
346  if (!c2.empty())
347  {
348  command = c2 + " --help";
349  }
350  else
351  {
352  map<string, string> capabilities;
353  factory->getCapabilities(capabilities);
354  map<string, string>::iterator iter = capabilities.begin();
355  cout<<"\nAvailable commands:\n"<<endl;
356  for (;iter != capabilities.end(); ++iter)
357  cout<<" "<<iter->first<<" -- "<<iter->second<<endl;
358  cout<<"\nUse option \"--help\" with above commands to get detailed tool command."<<endl;
359  status_ok = true;
360  break;
361  }
362  }
363  // Fetch OSSIM utility for requested operation:
364  ossimArgumentParser ap (command);
365  ossimString util_name = ap[0];
366  utility = factory->createTool(util_name);
367 
368  try
369  {
370  // Perform OSSIM command execution:
371  if (!utility.valid())
372  cout<<msg<<"Did not understand command <"<<util_name<<">"<<endl;
373  else if (!utility->initialize(ap))
374  cout<<msg<<"Could not execute command sequence <"<<command<<">."<<endl;
375  else if (!utility->helpRequested() && !utility->execute())
376  cout<<msg<<"Error encountered executing\n <"<<command <<">\nCheck options."<<endl;
377  else
378  status_ok = true;
379  }
380  catch (ossimException& x)
381  {
382  cout << msg << "Caught OSSIM exception: "<<x.what()<<endl;
383  }
384  catch (exception& x)
385  {
386  cout << msg << "Caught unknown exception: "<<x.what()<<endl;
387  }
388 
389  break;
390  }
391 
392  // Stop redirecting stdout and copy the output stream buffer to local memory:
393  dup2( savedStdout, fileno(stdout) );
394  int n = MAX_BUF_LEN;
395  string full_output;
396  while (n == MAX_BUF_LEN)
397  {
398  n = read(pipeDesc[0], m_buffer, MAX_BUF_LEN);
399  if (n > 0)
400  full_output.append(m_buffer, n);
401  }
402 
403  if (status_ok)
404  {
405  if (utility.valid() && !utility->helpRequested() && utility->isChipProcessor())
406  {
407  const char* response = "FILE ";
408  writeSocket(response, strlen(response));
409  ossimChipProcTool* ocp = (ossimChipProcTool*) utility.get();
410  ossimFilename prodFilename = ocp->getProductFilename();
411  sendFile(prodFilename);
412  }
413  else
414  {
415  const char* response = "TEXT ";
416  writeSocket(response, strlen(response));
417  writeSocket(full_output.c_str(), full_output.size());
418  if (!acknowledgeRcvd())
419  error("ERROR receiving acknowledge from client.");
420  }
421  }
422  else
423  {
424  const char* response = "ERROR";
425  writeSocket(response, strlen(response));
426  writeSocket(full_output.c_str(), full_output.size());
427  cout << "Sending ERROR to client and closing connection: <"<<full_output<<">"<<endl;
428  close(m_clisockfd);
429  }
430 
431  return status_ok;
432 }
433 
434 bool ossimToolServer::processOssimRequest(struct sockaddr_in& cli_addr)
435 {
436  char dst[INET6_ADDRSTRLEN];
437 
438  // TEST CODE:
439  cout << "\nprocessOssimRequest() -- Process ID: "<<getpid()<<endl;
440  cout << " Parent process ID: "<<getppid()<<endl;
441 
442  // Clear the input m_buffer and read the message sent:
443  memset(m_buffer, 0, MAX_BUF_LEN);
444  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" Waiting to recv"<<endl; //TODO REMOVE DEBUG
445  int n = recv(m_clisockfd, m_buffer, MAX_BUF_LEN, 0);
446  if (_DEBUG_) cout<<"ossimToolServer:"<<__LINE__<<" Received <"<<m_buffer<<">"<<endl; //TODO REMOVE DEBUG
447  if (n < 0)
448  error("ossimToolServer: EOF encountered reading from port");
449 
450  // If message received, acknowledge back:
451  if (n != 0)
452  {
453  // Log the message received:
454  void* addr = &(cli_addr.sin_addr);
455  if (!inet_ntop(AF_INET, addr, dst, INET6_ADDRSTRLEN))
456  error("ossimToolServer: Error returned from inet_ntop(). ");
457  cout << "\nossimToolServer: received message from: "<<dst<<"\n---------------\n"<<m_buffer
458  <<"\n---------------\n"<< endl;
459 
460  // process request:
461  ossimString command (m_buffer);
462  command.trim();
463 
464  if (command == "goodbye")
465  {
466  close(m_clisockfd);
467  return true;
468  }
469 
470  runCommand(command);
471  return true;
472  }
473  return false;
474 }
ossim_uint32 x
ossimString before(const ossimString &str, std::string::size_type pos=0) const
METHOD: before(str, pos) Returns string beginning at pos and ending one before the token str If strin...
std::basic_ostringstream< char > ostringstream
Class for char output memory streams.
Definition: ossimIosFwd.h:35
bool processOssimRequest(struct sockaddr_in &cli_addr)
bool runCommand(ossimString &command)
std::basic_ifstream< char > ifstream
Class for char input file streams.
Definition: ossimIosFwd.h:44
bool valid() const
Definition: ossimRefPtr.h:75
virtual void getCapabilities(std::map< std::string, std::string > &capabilities) const =0
Appends map with available utilities along with descriptions as <name, decription> pairs...
virtual bool isChipProcessor() const
Overrides base class implementation to indicate this class supports getChip() calls.
Definition: ossimTool.h:123
void startListening(const char *portid)
#define OINFO
static ossimToolRegistry * instance()
#define FORK_PROCESS
#define MAX_BUF_LEN
os2<< "> n<< " > nendobj n
const char * chars() const
For backward compatibility.
Definition: ossimString.h:77
ossimString trim(const ossimString &valueToTrim=ossimString(" \\)) const
this will strip lead and trailing character passed in.
void initSocket(const char *portid)
virtual bool initialize(ossimArgumentParser &ap)
Initializes from command line arguments.
Definition: ossimTool.cpp:58
virtual bool execute()=0
Writes product to output file if applicable.
#define _DEBUG_
bool helpRequested() const
Returns true when the initialization detects a "--help" option, so caller can avoid subsequent execut...
Definition: ossimTool.h:134
bool sendFile(const ossimFilename &fname)
bool empty() const
Definition: ossimString.h:411
ossimFilename file() const
virtual ossimTool * createTool(const std::string &typeName) const =0
ossimString after(const ossimString &str, std::string::size_type pos=0) const
METHOD: after(str, pos) Returns string immediately after the token str.
void writeSocket(const char *buf, int bufsize)
static void sigchld_handler(int s)
const ossimFilename & getProductFilename() const
void error(const char *msg)