OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
daemon.cc
Go to the documentation of this file.
1 // daemon.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <unistd.h> // for getopt fork setsid execvp access geteuid
34 #include <sys/wait.h> // for waitpid
35 #include <sys/types.h>
36 #include <sys/stat.h> // for chmod
37 #include <ctype.h> // for isdigit
38 
39 #include <fstream>
40 #include <iostream>
41 #include <string>
42 #include <cstring>
43 #include <cstdlib>
44 #include <cerrno>
45 
46 using std::ifstream ;
47 using std::ofstream ;
48 using std::cout ;
49 using std::endl ;
50 using std::cerr ;
51 using std::flush;
52 using std::string ;
53 
54 #include "config.h"
55 #include "ServerExitConditions.h"
56 #include "BESServerUtils.h"
57 #include "BESScrub.h"
58 
59 #define BES_SERVER_ROOT "BES_SERVER_ROOT"
60 #define BES_SERVER "/beslistener"
61 #define BES_SERVER_PID "/bes.pid"
62 
63 int daemon_init() ;
64 int mount_server( char ** ) ;
65 int pr_exit( int status ) ;
66 void store_listener_id( int pid ) ;
67 bool load_names( const string &install_dir, const string &pid_dir ) ;
68 
69 string NameProgram ;
70 
71 // This two variables are set by load_names
72 string server_name ;
74 
75 char **arguments = 0 ;
76 
77 int
78 main(int argc, char *argv[])
79 {
80 #ifndef BES_DEVELOPER
81  // must be root to run this app and to set user id and group id later
82  uid_t curr_euid = geteuid() ;
83  if( curr_euid )
84  {
85  cerr << "FAILED: Must be root to run BES" << endl ;
87  }
88 #else
89  cerr << "Developer Mode: not testing if BES is run by root" << endl ;
90 #endif
91 
92  NameProgram = argv[0] ;
93 
94  string install_dir ;
95  string pid_dir ;
96 
97  // If you change the getopt statement below, be sure to make the
98  // corresponding change in ServerApp.cc and besctl.in
99  int c = 0 ;
100 
101  // there are 16 arguments allowed to the daemon, including the program
102  // name. 3 options do not have arguments and 6 have arguments
103  if( argc > 16 )
104  {
105  // the show_usage method exits
107  }
108 
109  // argv[0] is the name of the program, so start num_args at 1
110  unsigned short num_args = 1 ;
111  while( ( c = getopt( argc, argv, "hvsd:c:p:u:i:r:" ) ) != EOF )
112  {
113  switch( c )
114  {
115  case 'v': // version
117  break ;
118  case '?': // unknown option
119  case 'h': // help
121  break ;
122  case 'i': // BES install directory
123  install_dir = optarg ;
124  if( BESScrub::pathname_ok( install_dir, true ) == false )
125  {
126  cout << "The specified install directory (-i option) "
127  << "is incorrectly formatted. Must be less than "
128  << "255 characters and include the characters "
129  << "[0-9A-z_./-]" << endl ;
130  return 1 ;
131  }
132  num_args+=2 ;
133  break ;
134  case 's': // secure server
135  num_args++ ;
136  break ;
137  case 'r': // where to write the pid file
138  {
139  pid_dir = optarg ;
140  if( BESScrub::pathname_ok( pid_dir, true ) == false )
141  {
142  cout << "The specified state directory (-r option) "
143  << "is incorrectly formatted. Must be less than "
144  << "255 characters and include the characters "
145  << "[0-9A-z_./-]" << endl ;
146  return 1 ;
147  }
148  num_args+=2 ;
149  }
150  break ;
151  case 'c': // configuration file
152  case 'u': // unix socket
153  {
154  string check_path = optarg ;
155  if( BESScrub::pathname_ok( check_path, true ) == false )
156  {
157  cout << "The specified install directory (-i option) "
158  << "is incorrectly formatted. Must be less than "
159  << "255 characters and include the characters "
160  << "[0-9A-z_./-]" << endl ;
161  return 1 ;
162  }
163  num_args+=2 ;
164  }
165  break ;
166  case 'p': // TCP port
167  {
168  string port_num = optarg ;
169  for( unsigned int i = 0; i < port_num.length(); i++ )
170  {
171  if( !isdigit( port_num[i] ) )
172  {
173  cout << "The specified port contains non-digit "
174  << "characters: " << port_num << endl ;
175  return 1 ;
176  }
177  }
178  num_args+=2 ;
179  }
180  break ;
181  case 'd': // debug
182  {
183  string check_arg = optarg ;
184  if( BESScrub::command_line_arg_ok( check_arg ) == false )
185  {
186  cout << "The specified debug options \"" << check_arg
187  << "\" contains invalid characters" << endl ;
188  return 1 ;
189  }
190  num_args+=2 ;
191  }
192  break ;
193  default:
195  break ;
196  }
197  }
198  // if the number of argumens is greater than the number of allowed arguments
199  // then extra arguments were passed that aren't options. Show usage and
200  // exit.
201  if( argc > num_args )
202  {
203  cout << NameProgram
204  << ": too many arguments passed to the BES" ;
205  BESServerUtils::show_usage( NameProgram ) ;
206  }
207 
208  if( pid_dir.empty() )
209  {
210  pid_dir = install_dir ;
211  }
212 
213  // Set the name of the listener and the file for the listenet pid
214  if( !load_names( install_dir, pid_dir ) )
215  return 1 ;
216 
217  // make sure the array size is not too big
218  if( BESScrub::size_ok( sizeof( char *), num_args+1 ) == false )
219  {
220  cout << NameProgram
221  << ": too many arguments passed to the BES" ;
222  BESServerUtils::show_usage( NameProgram ) ;
223  }
224  arguments = new char *[num_args+1] ;
225 
226  // Set arguments[0] to the name of the listener
227  string::size_type len = server_name.length() ;
228  char temp_name[len + 1] ;
229  server_name.copy( temp_name, len ) ;
230  temp_name[len] = '\0' ;
231  arguments[0] = temp_name ;
232 
233  // Marshal the arguments to the listener from the command line
234  // arguments to the daemon
235  for( int i = 1; i < num_args; i++ )
236  {
237  arguments[i] = argv[i] ;
238  }
239  arguments[num_args] = NULL ;
240 
241  if( !access( file_for_listener.c_str(), F_OK ) )
242  {
243  ifstream temp( file_for_listener.c_str() ) ;
244  cout << NameProgram
245  << ": there seems to be a BES daemon already running at " ;
246  char buf[500] ;
247  temp.getline( buf, 500 ) ;
248  cout << buf << endl ;
249  temp.close() ;
250  return 1 ;
251  }
252 
253  daemon_init() ;
254 
255  int restart = mount_server( arguments ) ;
256  if( restart == 2 )
257  {
258  cout << NameProgram
259  << ": server cannot mount at first try (core dump). "
260  << "Please correct problems on the process manager "
261  << server_name << endl ;
262  return 0 ;
263  }
264  while( restart )
265  {
266  sleep( 5 ) ;
267  restart = mount_server( arguments ) ;
268  }
269  delete [] arguments; arguments = 0 ;
270 
271  if( !access( file_for_listener.c_str(), F_OK ) )
272  {
273  (void)remove( file_for_listener.c_str() ) ;
274  }
275 
276  return 0 ;
277 }
278 
279 int
281 {
282  pid_t pid ;
283  if( ( pid = fork() ) < 0 )
284  return -1 ;
285  else if( pid != 0 )
286  exit( 0 ) ;
287  setsid() ;
288  return 0 ;
289 }
290 
291 int
293 {
294  const char *perror_string = 0 ;
295  pid_t pid ;
296  int status ;
297  if( ( pid = fork() ) < 0 )
298  {
299  cerr << NameProgram << ": fork error " ;
300  perror_string = strerror( errno ) ;
301  if( perror_string )
302  cerr << perror_string ;
303  cerr << endl ;
304  return 1 ;
305  }
306  else if( pid == 0 ) /* child process */
307  {
308  execvp( arguments[0], arguments ) ;
309  cerr << NameProgram
310  << ": mounting listener, subprocess failed: " ;
311  perror_string = strerror( errno ) ;
312  if( perror_string )
313  cerr << perror_string ;
314  cerr << endl ;
315  exit( 1 ) ;
316  }
317  store_listener_id( pid ) ;
318  if( ( pid = waitpid( pid, &status, 0 ) ) < 0 ) /* parent process */
319  {
320  cerr << NameProgram << ": waitpid error " ;
321  perror_string = strerror( errno ) ;
322  if( perror_string )
323  cerr << perror_string ;
324  cerr << endl ;
325  return 1 ;
326  }
327  int child_status = pr_exit( status ) ;
328  return child_status ;
329 }
330 
331 int
332 pr_exit(int status)
333 {
334  if( WIFEXITED( status ) )
335  {
336  int status_to_be_returned = SERVER_EXIT_UNDEFINED_STATE ;
337  switch( WEXITSTATUS( status ) )
338  {
340  status_to_be_returned = 0 ;
341  break ;
343  {
344  cerr << NameProgram
345  << ": server cannot start, exited with status "
346  << WEXITSTATUS( status ) << endl ;
347  cerr << "Please check all error messages "
348  << "and adjust server installation" << endl ;
349  status_to_be_returned = 0 ;
350  }
351  break;
353  {
354  cerr << NameProgram
355  << ": abnormal server termination, exited with status "
356  << WEXITSTATUS( status ) << endl ;
357  status_to_be_returned = 1 ;
358  }
359  break;
360  case SERVER_EXIT_RESTART:
361  {
362  cout << NameProgram
363  << ": server has been requested to re-start." << endl ;
364  status_to_be_returned = 1 ;
365  }
366  break;
367  default:
368  status_to_be_returned = 1 ;
369  break;
370  }
371 
372  return status_to_be_returned;
373  }
374  else if( WIFSIGNALED( status ) )
375  {
376  cerr << NameProgram
377  << ": abnormal server termination, signaled with signal number "
378  << WTERMSIG( status ) << endl ;
379 #ifdef WCOREDUMP
380  if( WCOREDUMP( status ) )
381  {
382  cerr << NameProgram << ": server dumped core." << endl ;
383  return 2 ;
384  }
385 #endif
386  return 1;
387  }
388  else if( WIFSTOPPED( status ) )
389  {
390  cerr << NameProgram
391  << ": abnormal server termination, stopped with signal number "
392  << WSTOPSIG( status ) << endl ;
393  return 1 ;
394  }
395  return 0 ;
396 }
397 
398 void
400 {
401  const char *perror_string = 0 ;
402  ofstream f( file_for_listener.c_str() ) ;
403  if( !f )
404  {
405  cerr << NameProgram << ": unable to create pid file "
406  << file_for_listener << ": " ;
407  perror_string = strerror( errno ) ;
408  if( perror_string )
409  cerr << perror_string ;
410  cerr << " ... Continuing" << endl ;
411  cerr << endl ;
412  }
413  else
414  {
415  f << "PID: " << pid << " UID: " << getuid() << endl ;
416  f.close() ;
417  mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ;
418  (void)chmod( file_for_listener.c_str(), new_mode ) ;
419  }
420 }
421 
422 bool
423 load_names( const string &install_dir, const string &pid_dir )
424 {
425  char *xdap_root = 0 ;
426  string bindir = "/bin";
427  if( !pid_dir.empty() )
428  {
429  file_for_listener = pid_dir ;
430  }
431 
432  if( !install_dir.empty() )
433  {
434  server_name = install_dir ;
435  server_name += bindir ;
436  if( file_for_listener.empty() )
437  {
438  file_for_listener = install_dir + "/var/run" ;
439  }
440  }
441  else
442  {
443  string prog = NameProgram ;
444  string::size_type slash = prog.find_last_of( '/' ) ;
445  if( slash != string::npos )
446  {
447  server_name = prog.substr( 0, slash ) ;
448  slash = prog.find_last_of( '/' ) ;
449  if( slash != string::npos )
450  {
451  string root = prog.substr( 0, slash ) ;
452  if( file_for_listener.empty() )
453  {
454  file_for_listener = root + "/var/run" ;
455  }
456  }
457  else
458  {
459  if( file_for_listener.empty() )
460  {
462  }
463  }
464  }
465  }
466 
467  if( server_name == "" )
468  {
469  server_name = "." ;
470  if( file_for_listener.empty() )
471  {
472  file_for_listener = "./run" ;
473  }
474  }
475 
478 
479  if( access( server_name.c_str(), F_OK ) != 0 )
480  {
481  cerr << NameProgram
482  << ": cannot start." << server_name << endl
483  << "Please either pass -i <install_dir> on the command line or "
484  << "set the environment variable " << BES_SERVER_ROOT << " "
485  << "to the installation directory where the BES listener is."
486  << endl ;
487  return false ;
488  }
489  return true ;
490 }
491 
static bool pathname_ok(const string &path, bool strict)
Does the string name a potentailly valid pathname? Test the given pathname to verfiy that it is a val...
Definition: BESScrub.cc:88
#define BES_SERVER
Definition: daemon.cc:60
#define SERVER_EXIT_NORMAL_SHUTDOWN
int main(int argc, char *argv[])
Definition: daemon.cc:78
#define BES_SERVER_PID
Definition: daemon.cc:61
static bool command_line_arg_ok(const string &arg)
sanitize command line arguments
Definition: BESScrub.cc:52
static void show_usage(const string &app_name)
void store_listener_id(int pid)
Definition: daemon.cc:399
#define SERVER_EXIT_ABNORMAL_TERMINATION
int pr_exit(int status)
Definition: daemon.cc:332
int mount_server(char **)
Definition: daemon.cc:292
string server_name
Definition: daemon.cc:72
#define SERVER_EXIT_FATAL_CAN_NOT_START
char ** arguments
Definition: daemon.cc:75
static void show_version(const string &app_name)
bool load_names(const string &install_dir, const string &pid_dir)
Definition: daemon.cc:423
#define BES_SERVER_ROOT
Definition: daemon.cc:59
string file_for_listener
Definition: daemon.cc:73
static bool size_ok(unsigned int sz, unsigned int nelem)
sanitize the size of an array.
Definition: BESScrub.cc:66
#define SERVER_EXIT_RESTART
#define SERVER_EXIT_UNDEFINED_STATE
int daemon_init()
Definition: daemon.cc:280
string NameProgram
Definition: daemon.cc:69