summaryrefslogtreecommitdiff
path: root/ACE/examples/OS/Process/imore.cpp
blob: ae3cf491fe13f02327bae6dbb85492a86667c52c (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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//=============================================================================
/**
 *  @file    imore.cpp (imore stands for indirect more.)
 *
 *  $Id$
 *
 *  This program demonstrates how to redirect stdout of a parent
 *  process to the stdin of its child process using either unnamed pipe
 *  or named pipes to relay data to subprocess which runs "more" to
 *  display data on the screen.  Run imore to see how to use this
 *  program.
 *
 *  Unfortunately, on Win32, this program doesn't use any pipe at all because
 *  using pipes confuses MORE.COM on Win32 and it just acts like "cat" on Unix.
 *
 *
 *  @author Nanbor Wang <nanbor@cs.wustl.edu>
 */
//=============================================================================


#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_errno.h"
#include "ace/OS_NS_unistd.h"
#include "ace/OS_NS_fcntl.h"
#include "ace/FIFO_Recv.h"
#include "ace/FIFO_Send.h"
#include "ace/Pipe.h"
#include "ace/Get_Opt.h"
#include "ace/Log_Msg.h"
#include "ace/Process.h"
#include "ace/Signal.h"



#if defined (ACE_WIN32)
static const ACE_TCHAR *executable = ACE_TEXT("MORE.COM");
#else
static const char * executable = "more"; // I like less better.
static const ACE_TCHAR *rendezvous_dir = ACE_TEXT("/tmp");
static const ACE_TCHAR *rendezvous_pfx = ACE_TEXT("imore");
#endif /* ACE_WIN32 */

static ACE_TCHAR *fname = 0;   // File you want to view.
static int use_named_pipe = 0; // Do we want to use named pipe?

static void
usage (void)
{
  ACE_ERROR ((LM_ERROR, "Usage: imore [-n|-u] <filename>\n"
              "\t-n Use named pipe.\n"
              "\t-u Use unnamed pipe.\n"));
}

static int
parse_args (int argc, ACE_TCHAR **argv)
{
  ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("un"));
  int c;

  while ((c = get_opt ()) != -1)
    {
    switch (c)
      {
      case 'n': // We want to use named pipe.
#if !defined (ACE_WIN32)
        use_named_pipe = 1;
#else
        ACE_ERROR_RETURN ((LM_ERROR, "Named pipes not supported on Win32\n"), -1);
#endif /* !ACE_WIN32 */
        break;
      case 'u':  // Use unnamed pipe.
        use_named_pipe = 0;
        break;
      default:  // What are you talking about?
        usage ();
        return -1;
      }
    }

  if (get_opt.opt_ind () >= argc) // Do you forget to give me a filename to "more?"
    {
      usage ();
      return -1;
    }
  else
    fname = argv[get_opt.opt_ind ()]; // Alright.

  return 0;
}

#if !defined (ACE_WIN32) && !defined (ACE_DISABLE_TEMPNAM)
static int
setup_named_pipes (ACE_Process_Options &opt)
{
  // Create a unique temporary name for named pipe.
  ACE_TCHAR *rendezvous = ACE_OS::tempnam (rendezvous_dir,
                                           rendezvous_pfx);

  // Out of memory?
  if (rendezvous == 0)
    return -1;

  // Alright, this is indeed strange.  Named pipes are meant to be
  // used for unrelated processes.  Because of the constraints in
  // ACE_Process, I have to pre-open the named pipes here.
  ACE_FIFO_Recv rfifo;          // read end fifo.
  ACE_FIFO_Send wfifo;          // write end fifo.

  // Check if the pipes are created successfully.
  if (rfifo.open (rendezvous) == -1 || wfifo.open (rendezvous) == -1)
    {
      ACE_OS::free (rendezvous);
      ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "fifo.open"), -1);
    }

  // Remove (rm, del) the file after no one uses it any more.
  ACE_OS::unlink (rendezvous);
  ACE_OS::free (rendezvous);

  // Setting up pipe between parent and child process.  Use the read
  // end of the named pipe as child process'es ACE_STDIN.
  // ACE_Process_Options will keep copies (by dup) of fd's that we
  // pass in.  Notice that we have to specify child process to use
  // ACE_STDOUT for output explicitly because we'll close it down in
  // the line after.  Child process will use whatever we use to dup2
  // ACE_STDOUT as its stdout.
  opt.set_handles (rfifo.get_handle (), ACE_STDOUT);

  // The previous keep a copy of original ACE_STDOUT fd, now we
  // can replace ACE_STDOUT of parent process to the write end
  // of the named pipe.
  ACE_OS::dup2 (wfifo.get_handle (), ACE_STDOUT);

  // Close unused fd's.  Notice ACE_FIFO doesn't close the fd
  // when it goes out of scope.
  rfifo.close ();
  wfifo.close ();
  return 0;
}
#endif

#if !defined (ACE_WIN32)
static int
setup_unnamed_pipe (ACE_Process_Options &opt)
{
  // Create an unnamed pipe instance.
  ACE_Pipe pipe;

  // Check if the pipe is created successfully.
  if (pipe.open () == -1)
    ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "pipe.open"), -1);

  // Setting up pipe between parent and child process.  Use the pipe
  // as child process'es ACE_STDIN.  ACE_Process_Options will keep
  // copies (by dup) of fd's that we pass in.  Notice that we have to
  // specify child process to use ACE_STDOUT for output explicitly
  // because we'll close it down in the line after.  Child process
  // will use whatever we use to dup2 ACE_STDOUT as its stdout.
  opt.set_handles (pipe.read_handle (), ACE_STDOUT);

  // The previous keep a copy of original ACE_STDOUT fd, now we
  // can replace ACE_STDOUT of parent process to the pipe.
  ACE_OS::dup2 (pipe.write_handle (), ACE_STDOUT);

  // Don't forget to close the unused fd.
  pipe.close ();
  return 0;
}
#endif

#if !defined (ACE_WIN32)
static int
print_file (ACE_HANDLE infd)
{
  char buffer[BUFSIZ];
  ssize_t len;

  while ((len = ACE_OS::read (infd, buffer, BUFSIZ)) > 0)
    {
      if ((ACE_OS::write (ACE_STDOUT, buffer, len) != len))
        {
          if (errno == EPIPE)
           {
              // I tried to "produce" EPIPE warning to test
              // the program but never seen one.  (odd.)
              // ACE_ERROR ((LM_ERROR, "\n\nEPIPE\n"));
              break;
            }
          else
            {
              ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "write"), -1);
            }
        }
    }

  return 0;
}
#endif

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  // Ignore SIGPIPE signal on Unix platforms in case
  // child process (more) terminates before we finish
  // writing to stdout.
#if !defined (ACE_WIN32)
  ACE_Sig_Action sig_act (SIG_IGN);
  if (sig_act.register_action (SIGPIPE) == -1)
    ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_Sig_Action::register_action"), -1);
#endif /* ACE_WIN32 */

  // Alright, what you want me to do now?
  if (::parse_args (argc, argv) == -1)
    return -1;

  // Can I find the file you want?
  ACE_HANDLE infile = ACE_OS::open (fname, O_RDONLY);
  if (infile == ACE_INVALID_HANDLE)
      ACE_ERROR_RETURN ((LM_DEBUG, "%p\n", fname), -1);

  ACE_Process new_process;

  // The ACE_Process_Options does not need to be enclosed in a block
  // because it does not close the file handles, the ACE_Process closes
  // them upon destruction.
#if !defined (ACE_WIN32)
  ACE_Process_Options options;

  if (use_named_pipe)
    {
#  if defined (ACE_DISABLE_TEMPNAM)
      ACE_ERROR_RETURN ((LM_ERROR,
                         "ACE_DISABLE_TEMPNAM set; can't use named pipes\n"),
                        -1);
#  else
      if (::setup_named_pipes (options) == -1)
        ACE_ERROR_RETURN ((LM_ERROR, "Error, bailing out!\n"), -1);
#  endif /* ACE_DISABLE_TEMPNAM */
    }
  else
    {
      if (::setup_unnamed_pipe (options) == -1)
        ACE_ERROR_RETURN ((LM_ERROR, "Error, bailing out!\n"), -1);
    }

  options.command_line (executable);
  if (new_process.spawn (options) == -1)
    {
      int error = ACE_OS::last_error ();
      ACE_ERROR_RETURN ((LM_ERROR, "%p errno = %d.\n",
                         "test_more", error), -1);
    }

  // write file to ACE_STDOUT.
  if (::print_file (infile) == -1)
    ACE_ERROR_RETURN ((LM_ERROR, "Error, bailing out!\n"), -1);

  // Close the STDOUT to inform child eof.
  ACE_OS::close (ACE_STDOUT);
#else
  // We can only pass a file handler directly to child process
  // otherwise "more" doesn't act quite the way we want.  Nonetheless,
  // if your child process don't need to interact with the terminal,
  // we can use the exact code for Unixes on NT.
  ACE_Process_Options options;
  options.command_line (executable);
  options.set_handles (infile);
  if (new_process.spawn (options) == -1)
    {
      int error = ACE_OS::last_error ();
      ACE_ERROR_RETURN ((LM_ERROR, "%p errno = %d.\n",
                         "test_more", error), -1);
    }
#endif /* ! ACE_WIN32 */

  // Wait till we are done.
  ACE_exitcode status;
  new_process.wait (&status);
  ACE_DEBUG ((LM_DEBUG, "Process exit with status %d\n", status));

  ACE_OS::close (infile);

  return 0;
}