Re: Bash command completion when not connected to a terminal

2007-06-08 Thread raner


raner wrote:
> 
> Is there a way to configure bash to echo back each character that was sent
> to it's stdin?
> 

All right, I spent quite some time with this problem, so I thought I share
what I've found out and answer my own question. First of all, I should
probably rephrase my original question. The real issue here is: How can I
enable input echoing for a Solaris bash that is not connected to a terminal?

I installed bash 3.2 on Solaris and compared the results with Cygwin bash
3.2 on Windows XP. When launched by my Java application, the Cygwin bash
will echo the user input, and the Solaris bash will not. I looked at the
bash source code and found out that the input echoing is done by GNU
readline. If you search rltty.c for the variable readline_echoing_p you will
see that it is initialized based on the ECHO flag in the terminal's TIOTYPE
structure (which is just an alias for struct termio/termios). As the bash
process is not connected to a terminal in my case (the original root of the
problem), I assume that the struct termio/termios is probably all
zero/undefined and doesn't have the ECHO flag set. Something like that, I
didn't really dig into the details. It looks like, on Solaris, bash will not
echo its input unless it is connected to a terminal. If someone in fact does
know a way to achieve this, I'd still like to hear it, though.

So, what is the work-around? Under Solaris, the situation can be summarized
as "No terminal, no echo". So, to get echo to work, we need to run bash with
a terminal, or to be more precise, with a pseudo-terminal. I was under the
illusion that there would be a simple Solaris command to create a
pseudo-terminal (maybe something like "pty bash"), but, of course, there
isn't. After googling around a bit, I found a utility called "empty"
(http://empty.sf.net). It is designed for a slightly different purpose than
what I needed, but it can execute a command in a pseudo-terminal and makes
stdin/stdout available as named pipes. To run a bash with a pseudo-terminal
in my Java application, I had to write a little script that I called
ptybash:

#!/bin/sh
./empty -f -i pty.in -o pty.out bash -i
cat -u pty.in

The script uses empty to create the pseudo-terminal and the named pipes. To
connect the current stdin/stdout with the pipes, it uses two separate cat
commands. My Java application now doesn't just launch bash but instead:

bash ptybash

The first bash, of course, will still suffer from the echo issue, but it
only serves to run the ptybash script, which launches a second bash in a
pseudo-terminal. That second bash should have its input echoing intact. Or
so I thought... Because, when I tried it, again, all my typed commands were
dutyfully executed, but I still couldn't see what I typed. However, now that
the bash was running in a terminal, I could actually type:

stty echo

And after this, I could finally see what I typed, and the completion also
showed me the completed filenames. It's a little bit nonstraightforward, but
it solved the problem. Does anybody know of an easier way to do this?

-- 
View this message in context: 
http://www.nabble.com/Bash-command-completion-when-not-connected-to-a-terminal-tf3876564.html#a11022070
Sent from the Gnu - Bash mailing list archive at Nabble.com.



___
Bug-bash mailing list
Bug-bash@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-bash


Suggestion and patch for tcp-server-sockets

2007-06-08 Thread Ralf Goertz
Hi,

the other day I asked for a reason why there are no server sockets for
bash. Since I got no answer, I decided to do it myself. I included a
patch against the unpatched 3.2 file lib/sh/netopen.c (I think that file
has not been patched by any of the 17 official patches). I think it is
not reasonable to have udp-server-sockets so I omitted them (it would
have been much more complicated to include them anyway).

Now, if you want to open a server socket just do

exec 5<>/dev/tcp//port

Here is the patch


--- bash-3.2/lib/sh/netopen.c   2006-08-02 23:20:30.0 +0200
+++ bash-3.2-serversocketpatch/lib/sh/netopen.c 2007-06-08 
12:14:46.0 +0200
@@ -70,8 +70,10 @@
 static int _getaddr __P((char *, struct in_addr *));
 static int _getserv __P((char *, int, unsigned short *));
 static int _netopen4 __P((char *, char *, int));
+static int _netopenserver4 __P((char *));
 #else /* HAVE_GETADDRINFO */
 static int _netopen6 __P((char *, char *, int));
+static int _netopenserver6 __P((char *));
 #endif
 
 static int _netopen __P((char *, char *, int));
@@ -200,6 +202,65 @@
 
   return(s);
 }
+
+static int 
+_netopenserver4(serv)
+ char *serv;
+{
+  struct sockaddr_in sin;
+  unsigned short p;
+  int s, e, ls, sockopt; /*ls is the listening socket */
+  struct linger fix_ling; /* as suggested by netcat */
+  char *errstring, *errstrings[]={"bind","listen","accept"};
+
+  if (_getserv(serv, 't', &p) == 0)
+{
+  internal_error(_("%s: invalid service"), serv);
+  errno = EINVAL;
+  return -1;
+}
+   
+  memset ((char *)&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_port = p;
+
+  ls = socket(AF_INET, SOCK_STREAM, 0);
+  if (ls < 0)
+{
+  sys_error ("socket");
+  return (-1);
+}
+  if (setsockopt(ls, SOL_SOCKET, SO_LINGER, &fix_ling, sizeof(fix_ling) 
< 0)); /* FIXME what to do if this fails */
+  if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt) 
< 0)); /* FIXME what to do if this fails */
+  
+  if (bind(ls, (struct sockaddr *) &sin, sizeof(sin)) < 0)
+{
+  errstring=errstrings[0];
+  goto err;
+}
+  
+  if (listen(ls,1) < 0)
+{
+  errstring=errstrings[1];
+  goto err;
+}
+  
+  s = accept(ls,NULL,NULL);
+  if (s<0) 
+{
+  errstring=errstrings[2];
+  goto err;
+}
+  close(ls);
+  return(s);
+err:
+  e = errno;
+  sys_error(errstring);
+  close(ls);
+  errno = e;
+  return (-1);
+}
+
 #endif /* ! HAVE_GETADDRINFO */
 
 #ifdef HAVE_GETADDRINFO
@@ -266,6 +327,95 @@
 }
   return s;
 }
+/*
+ * Open a TCP server socket on port SERV.  Uses getaddrinfo(3) which
+ * provides support for IPv6.  Returns the connected socket or -1 on
+ * error.
+ */
+static int
+_netopenserver6 (serv)
+ char *serv;
+{
+  int s, e, ls, sockopt; /* ls is the listening socket */
+  struct linger fix_ling; /* as suggested by netcat */
+  struct addrinfo hints, *res, *res0;
+  int gerr,retval;
+  char *errstring, *errstrings[]={"bind","listen","accept"};
+
+  memset ((char *)&hints, 0, sizeof (hints));
+  /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */
+#ifdef DEBUG   /* PF_INET is the one that works for me */
+  hints.ai_family = PF_INET;
+#else
+  hints.ai_family = PF_UNSPEC;
+#endif
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;
+  fix_ling.l_onoff = 1;
+  fix_ling.l_linger = 0;
+
+
+  gerr = getaddrinfo (NULL, serv, &hints, &res0);
+  if (gerr)
+{
+  if (gerr == EAI_SERVICE)
+   internal_error ("%s: %s", serv, gai_strerror (gerr));
+  else
+   internal_error ("%s: %s", "localhost", gai_strerror (gerr));
+  errno = EINVAL;
+  return -1;
+}
+
+  for (res = res0; res; res = res->ai_next)
+{
+  if ((ls = socket (res->ai_family, res->ai_socktype, 
res->ai_protocol)) < 0)
+   {
+ if (res->ai_next)
+   continue;
+ sys_error ("socket");
+ freeaddrinfo (res0);
+ return -1;
+   }
+  retval = setsockopt(ls, SOL_SOCKET, SO_LINGER, &fix_ling, 
sizeof(fix_ling)); /* FIXME what to do if this fails */
+  retval = setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &sockopt, 
sizeof(sockopt)); /* FIXME what to do if this fails */
+  retval = bind(ls, res->ai_addr, res->ai_addrlen);
+  if (retval<0) 
+{
+errstring=errstrings[0];
+goto err;
+}
+  retval = listen(ls,1);
+  if (retval<0) 
+{
+errstring=errstrings[1];
+goto err;
+}
+  s = accept(ls,NULL,NULL);
+  if (s<0) 
+{
+errstring=errstrings[2];
+goto err;
+}
+  close(ls);
+  freeaddrinfo (res0);
+  break;
+   
+err:  
+  if (res->ai_next)
+   {
+ close (ls);
+ continue;
+   }
+ e = errno;
+ sys_error (errstring);
+ close (ls);
+ freeaddrinfo (res0);
+ errno = e;
+ return -1;
+   }
+  return s;
+}
+
 #endif /* HAVE_GETADDRINFO */
 
 /*
@@ -279,9 +