summaryrefslogtreecommitdiff
path: root/examples/C++NPv1/Process_Per_Connection_Logging_Server.cpp
blob: 4b831dc47919422d48e76097fb400eee57b7eee3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
** $Id$
**
** Copyright 2001 Addison Wesley. All Rights Reserved.
*/

#include "ace/Log_Msg.h"
#include "ace/Process_Manager.h"
#include "ace/Signal.h"
#include "ace/OS_NS_string.h"
#include "ace/os_include/os_fcntl.h"

#include "Process_Per_Connection_Logging_Server.h"
#include "Logging_Handler.h"

#include <errno.h>

namespace {
  extern "C" void sigterm_handler (int /* signum */) { /* No-op. */ }
}


Logging_Process::Logging_Process (const char *prog_name,
                                  const ACE_SOCK_Stream &logging_peer)
    : logging_peer_ (logging_peer.get_handle ())
{
  ACE_OS::strcpy (prog_name_, prog_name);
}

// Set up the process options here. If the decision to do a fork
// a no exec on POSIX needs to be changed, this is the only place
// that needs to change (omit the creation_flags() call).
// We request that the logging client's socket handle be passed
// to the child process. The internals of ACE_Process insure that
// it gets put on the command line if starting a new program image,
// and that if it needed to be duplicated to accomplish that (such
// as on Win32) it will get properly closed.
// The process_name () call sets the program to run and is also used
// for the fork() call on POSIX.
// avoid_zombies has a real affect only on POSIX; it's harmless on Win32.
// Setting the NO_EXEC creation flag is what prevents the exec() on
// POSIX. It has no affect on Win32.
int
Logging_Process::prepare (ACE_Process_Options &options)
{
  if (options.pass_handle (logging_peer_.get_handle ()) == -1)
    ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "pass_handle"), -1);
  options.command_line ("%s", prog_name_);
  options.avoid_zombies (1);
  options.creation_flags (ACE_Process_Options::NO_EXEC);
  return 0;
}

// Just delete the process object. If any handles needed to be
// duplicated to be passed to the child, they'll get closed now
// by the ACE_Process destructor.
void
Logging_Process::unmanage ()
{
  delete this;
}


int
Process_Per_Connection_Logging_Server::handle_connections ()
{
  ACE_SOCK_Stream logging_peer;

  // Block until a client connects.
  if (acceptor ().accept (logging_peer) == -1)
    return -1;

  Logging_Process *logger =
      new Logging_Process (prog_name_, logging_peer);
  ACE_Process_Options options;
  pid_t pid;
  pid = ACE_Process_Manager::instance ()->spawn (logger,
                                                 options);
  // If we came back with pid 0 from the spawn(), this is a
  // POSIX fork system - we are in the child process. Handle the
  // logging records, then exit.
  if (pid == 0) {
    acceptor().close ();
    handle_data (&logging_peer);
    delete logger;
    ACE_OS::exit (0);
  }
  logging_peer.close ();
  if (pid == -1)
    ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "spawn()"), -1);

  // See if there are any child processes that have
  // exited - reap their status and clean up handles held
  // open while the child executed.
  ACE_Process_Manager::instance ()->wait (0,
                                          ACE_Time_Value::zero);
  return 0;
}

int
Process_Per_Connection_Logging_Server::handle_data (ACE_SOCK_Stream *client)
{
  // Disable non-blocking mode.
  client->disable (ACE_NONBLOCK);
  ACE_FILE_IO log_file;
  make_log_file (log_file, client);
  Logging_Handler logging_handler (log_file, *client);

  while (logging_handler.log_record () != -1)
    continue;

  log_file.close ();
  return 0;
}


int
Process_Per_Connection_Logging_Server::run (int argc, char *argv[])
{
  ACE_OS::strncpy (prog_name_, argv[0], MAXPATHLEN);
  prog_name_[MAXPATHLEN] = '\0'; // Ensure NUL-termination.
  // If there are 2 command line arguments after prog_name_, this
  // is a spawned worker process. Else run as the master.
  if (argc == 3)
    return run_worker (argc, argv);  // Only on Win32.
  else
    return run_master (argc, argv);
}

int
Process_Per_Connection_Logging_Server::run_master (int argc, char *argv[])
{
  u_short logger_port = 0;
  if (argc == 2)
    logger_port = ACE_OS::atoi (argv[1]);
  if (this->open (logger_port) == -1)
    return -1;

  for (;;)
    if (handle_connections () == -1)
      return -1;

  ACE_NOTREACHED (return 0;)
}

int
Process_Per_Connection_Logging_Server::run_worker (int, char *argv[])
{
  intptr_t client_handle_i = ACE_OS::atoi (argv[2]);
  // Some compilers don't like reinterpret_casting an int to an int, so
  // only do reinterpret_cast on Windows.
#if defined (ACE_WIN32)
  ACE_HANDLE client_handle =
    reinterpret_cast<ACE_HANDLE> (client_handle_i);
#else
  ACE_HANDLE client_handle =
    static_cast<ACE_HANDLE> (client_handle_i);
#endif /* ACE_WIN32 */
  ACE_SOCK_Stream client (client_handle);

  handle_data (&client);
  client.close ();
  return 0;
}


int ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  // Register to receive the <SIGTERM> signal.
  ACE_Sig_Action sa ((ACE_SignalHandler)sigterm_handler,
                     SIGTERM);

  Process_Per_Connection_Logging_Server server;

  if (server.run (argc, argv) == -1 && errno != EINTR)
    ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "server.run()"), 1);

  // Barrier synchronization.
  return ACE_Process_Manager::instance ()->wait ();
}