views:

948

answers:

2

Hi,

i need to use bash shell "inside" C# program. I want to mimic user typing in interactive mode and running cygwin commands.

i created a process that runs bash and redirect stdin,stout and std error but i can;t get tty to work attached is a sample code that starts bash process and redirect the input/output.

the problem is that i don't have tty device. if i try to run tty command or stty command i receive error response

tty - not a tty 
stty - Inappropriate ioctl for device

i think the this is caused from psi.UseShellExecute = false;

i need to run cygwin and disable echo with stty -echo but to do this i need a tty device. how ca i create a cygwin bash shell with tty device and redirect the stdin, out and error ?

1) what am missing ?

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace shartCygwin
{
    class Program
    {
        private static Queue<string> ResponseQueue = null;
        private static ManualResetEvent ResponseEvent = null;

        static void Main(string[] args)
        {
            ResponseQueue = new Queue<string>();
            ResponseEvent = new ManualResetEvent(false);

            Process bashProcess = new Process();

            bashProcess.StartInfo.FileName = "C:\\cygwin\\bin\\bash.exe"; 
            bashProcess.StartInfo.Arguments = "--login -i ";  
            bashProcess.StartInfo.WorkingDirectory = "C:\\cygwin\\bin";

            bashProcess.StartInfo.EnvironmentVariables["CYGWIN"] = "tty";

            bashProcess.StartInfo.RedirectStandardError = true;
            bashProcess.StartInfo.RedirectStandardInput = true;
            bashProcess.StartInfo.RedirectStandardOutput = true;
            bashProcess.StartInfo.CreateNoWindow = true;
            bashProcess.StartInfo.UseShellExecute = false;
            bashProcess.StartInfo.ErrorDialog = false;

            bashProcess.Start();

            DataReceivedEventHandler errorEventHandler = new DataReceivedEventHandler(ErrorDataReceived);
            DataReceivedEventHandler outEventHandler = new DataReceivedEventHandler(OutDataReceived);
            bashProcess.OutputDataReceived += outEventHandler;
            bashProcess.ErrorDataReceived += errorEventHandler;
            bashProcess.BeginErrorReadLine();
            bashProcess.BeginOutputReadLine();

            while(true)
            {
                Thread.Sleep(1000);
            }
        }

        static void ErrorDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs)
        {
            try
            {
                lock (ResponseQueue)
                {
                    Console.WriteLine(dataReceivedEventArgs.Data);
                    ResponseQueue.Enqueue(dataReceivedEventArgs.Data);
                    ResponseEvent.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Data);
            }
        }

        static void OutDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs)
        {
            try
            {
                lock (ResponseQueue)
                {
                    Console.WriteLine(dataReceivedEventArgs.Data);
                    ResponseQueue.Enqueue(dataReceivedEventArgs.Data);
                    ResponseEvent.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Data);
            }
        }
    }
}
+1  A: 

A side note, not a real answer, have a look at: http://www.codeproject.com/KB/IP/sharpssh.aspx

To answer the question:

Your not correctly handling the events... You need to look for e.Data == null in the event handler for Error/Output received. Once both event handlers receive this event AND the process has terminated you are done. Thus you wait on three handles, one to tell you the Process.Exited event fired, one to tell you the error output received null, one to tell you the output received null. Be sure to also set:

process.EnableRaisingEvents = true;

Here is the full answer redirecting output to current console:

 static int RunProgram(string exe, params string[] args)
 {
  ManualResetEvent mreProcessExit = new ManualResetEvent(false);
  ManualResetEvent mreOutputDone = new ManualResetEvent(false);
  ManualResetEvent mreErrorDone = new ManualResetEvent(false);

  ProcessStartInfo psi = new ProcessStartInfo(exe, String.Join(" ", args));
  psi.WorkingDirectory = Environment.CurrentDirectory;

  psi.RedirectStandardError = true;
  psi.RedirectStandardOutput = true;
  psi.CreateNoWindow = true;
  psi.UseShellExecute = false;
  psi.ErrorDialog = true;

  Process process = new Process();
  process.StartInfo = psi;

  process.Exited += delegate(object o, EventArgs e)
  {
   Console.WriteLine("Exited.");
   mreProcessExit.Set();
  };
  process.OutputDataReceived += delegate(object o, DataReceivedEventArgs e) 
  {
   if( e.Data != null )
    Console.WriteLine("Output: {0}", e.Data); 
   else
    mreOutputDone.Set(); 
  };
  process.ErrorDataReceived += delegate(object o, DataReceivedEventArgs e)
  {
   if (e.Data != null)
    Console.Error.WriteLine("Error: {0}", e.Data);
   else
    mreErrorDone.Set();
  };

  process.EnableRaisingEvents = true;
  Console.WriteLine("Start: {0}", process.StartInfo.FileName); 
  process.Start();
  process.BeginErrorReadLine();
  process.BeginOutputReadLine();

  if (process.HasExited) 
   mreProcessExit.Set();

  while(!WaitHandle.WaitAll(new WaitHandle[] { mreErrorDone, mreOutputDone, mreProcessExit }, 100))
   continue;
  return process.ExitCode;
 }
csharptest.net
the problem is that i don't have tty device.if i try to run tty command or stty command i receive error response tty - not a ttystty - Inappropriate ioctl for devicei think the this is caused from psi.UseShellExecute = false;i need to run cygwin and disable echo with stty -echo but to do this i need a tty device.how ca i create a cygwin bash shell with tty device and redirect the stdin, out and error ?
A: 

This may or may not help you or anybody else who happens across this question. This is the answer given to the same exact question on the Cygwin mailing list.

> i created a process that runs bash and redirect stdin,stout and std error
> but I can’t get tty to work attached is a sample code that starts bash
> process and redirect the input/output.
> the problem is that i don't have tty device. if i try to run tty command or
> stty command i receive error response 
> tty - not a tty 
> stty - Inappropriate ioctl for device

> i need to run cygwin and disable echo with stty -echo but to do this i need
> a tty device. how can i create a cygwin bash shell with tty device and
> redirect the stdin, out and error ?

  Why exactly do you think you need to run stty and set the tty operating
parameters, when the bash process is quite plainly *not* connected to a tty,
it is connected to your C# application?

  It's your application that is in charge of I/O - if it doesn't want echo,
all it has to do is discard the stuff it reads from the process' stdout
instead of displaying it, in your OutDataReceived/ErrorDataReceived handlers.

>             bashProcess.StartInfo.EnvironmentVariables["CYGWIN"] = "tty";

  Don't do this.  Win32 native processes don't understand Cygwin's tty
emulation, which is based on pipes.

    cheers,
      DaveK

Source: http://www.cygwin.com/ml/cygwin/2009-09/msg00637.html

kmark937