// ============================================================================ // $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 // // ============================================================================ #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] \n" "\t-n Use named pipe.\n" "\t-u Use unnamed pipe.\n")); } static int parse_args (int argc, ACE_TCHAR **argv) { ACE_Get_Arg_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; }