diff options
Diffstat (limited to 'ACE/examples/OS/Process/imore.cpp')
-rw-r--r-- | ACE/examples/OS/Process/imore.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/ACE/examples/OS/Process/imore.cpp b/ACE/examples/OS/Process/imore.cpp new file mode 100644 index 00000000000..181254e0cb4 --- /dev/null +++ b/ACE/examples/OS/Process/imore.cpp @@ -0,0 +1,267 @@ +// ============================================================================ +// $Id$ +// +// = LIBRARY +// examples +// +// = FILENAME +// imore.cpp (imore stands for indirect more.) +// +// = DESCRIPTION +// 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" + +ACE_RCSID(Process, imore, "$Id$") + +#if defined (ACE_WIN32) +static const ACE_TCHAR *executable = ACE_TEXT("MORE.COM"); +static const ACE_TCHAR *rendezvous_dir = ACE_TEXT("c:/temp"); +static const ACE_TCHAR *rendezvous_pfx = ACE_TEXT("imore"); +#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; +} + +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 == NULL) + 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; +} + +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; +} + +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; +} + +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; + + // Notice that we must enclose ACE_Process_Options in the block + // so the file handlers it keeps can be close elegantly. +#if !defined (ACE_WIN32) + { + ACE_Process_Options options; + + if ((use_named_pipe ? ::setup_named_pipes : + ::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; +} |