Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: 3.1.5-pws-4: zftp test function



Here's my own parting shot:  it's an addition I felt zftp needed.  Now
`zftp test' will poll/select the control connection and read a message
if necessary.  This is useful if the server has timed out and sent a
message like `421 Timeout, closing control connection', which
otherwise you don't know about until you send another command.  So if
you put something like
  [[ -n $ZFTP_HOST && zftp test ]]
into precmd, you know in advance when the connection's gone.  Using
this, it's also possible to implement transparent re-opening of the
control connection (like Ange-FTP does) in shell functions, but for
now that is an exercise for the reader.

You need select() or poll() for this to work.  It's hard to imagine
there's a system out there which has the socket interface but not
select(), but I can't be sure.  I used poll() in preference because at
least one manual page said it was the one to use.

There might be an argument for making modules able to add precmd
wrappers without any reference to a shell function, i.e. pure C hooks
to be run at various points.  Looking at preprompt() in utils.c, it
ought to be possible to divide it up that way.  Then you could get
warnings about a closed connection automatically when you get warnings
about new mail etc.  But there's no real difficulty leaving this up to
the user.

*** Doc/Zsh/mod_zftp.yo.zftp4	Mon Dec 14 17:40:57 1998
--- Doc/Zsh/mod_zftp.yo	Fri Dec 18 18:54:26 1998
***************
*** 92,97 ****
--- 92,118 ----
  --- here, the connection is restricted to a background subshell and
  you are free to open a simultaneous connection in the foreground.
  )
+ item(tt(test))(
+ Test the connection; if the server has reported
+ that it has closed the connection (maybe due to a timeout), return
+ status 2; if no connection was open anyway, return status 1; else
+ return status 0.  The tt(test) subcommand is
+ silent, apart from messages printed by the tt($ZFTP_VERBOSE)
+ mechanism, or error messages if the connection closes.  There is no
+ network overhead for this test.
+ 
+ The test is only supported on systems with either the tt(select(2)) or
+ tt(poll(2)) system calls; otherwise the message tt(not
+ supported on this system) is printed instead.
+ 
+ It is useful to put the code
+ 
+ nofill(tt([[ -n $ZFTP_HOST ]] && zftp test))
+ 
+ into the shell function tt(precmd) for testing the connection before
+ every prompt.  However, tt(zftp) will call tt(test) at the start of any
+ other subcommand when a connection is open.
+ )
  item(tt(cd) var(directory))(
  Change the remote directory to var(directory).  Also alters the shell
  variable tt(ZFTP_PWD).
*** Src/Modules/zftp.c.zftp4	Thu Dec 17 12:17:02 1998
--- Src/Modules/zftp.c	Sat Dec 19 14:51:48 1998
***************
*** 62,70 ****
  /* it's a TELNET based protocol, but don't think I like doing this */
  #include <arpa/telnet.h>
  
  /* pinch the definition from <netinet/in.h> for deficient headers */
  #ifndef INADDR_NONE
! #define INADDR_NONE 0xffffffff
  #endif
  
  /*
--- 62,80 ----
  /* it's a TELNET based protocol, but don't think I like doing this */
  #include <arpa/telnet.h>
  
+ /*
+  * We use poll() in preference to select because some subset of manuals says
+  * that's the thing to do, plus it's a bit less fiddly.  I don't actually
+  * have access to a system with poll but not select, however, though
+  * both bits of the code have been tested on a machine with both.
+  */
+ #ifdef HAVE_POLL_H
+ # include <poll.h>
+ #endif
+ 
  /* pinch the definition from <netinet/in.h> for deficient headers */
  #ifndef INADDR_NONE
! # define INADDR_NONE 0xffffffff
  #endif
  
  /*
***************
*** 124,130 ****
      ZFTP_HERE  = 0x0100,	/* here rather than over there */
      ZFTP_CDUP  = 0x0200,	/* CDUP rather than CWD */
      ZFTP_REST  = 0x0400,	/* restart: set point in remote file */
!     ZFTP_RECV  = 0x0800		/* receive rather than send */
  };
  
  typedef struct zftpcmd *Zftpcmd;
--- 134,141 ----
      ZFTP_HERE  = 0x0100,	/* here rather than over there */
      ZFTP_CDUP  = 0x0200,	/* CDUP rather than CWD */
      ZFTP_REST  = 0x0400,	/* restart: set point in remote file */
!     ZFTP_RECV  = 0x0800,	/* receive rather than send */
!     ZFTP_TEST  = 0x1000		/* test command, don't test */
  };
  
  typedef struct zftpcmd *Zftpcmd;
***************
*** 134,139 ****
--- 145,151 ----
      { "params", zftp_params, 0, 4, 0 },
      { "login", zftp_login, 0, 3, ZFTP_CONN },
      { "user", zftp_login, 0, 3, ZFTP_CONN },
+     { "test", zftp_test, 0, 0, ZFTP_TEST },
      { "cd", zftp_cd, 1, 1, ZFTP_CONN|ZFTP_LOGI },
      { "cdup", zftp_cd, 0, 0, ZFTP_CONN|ZFTP_LOGI|ZFTP_CDUP },
      { "dir", zftp_dir, 0, -1, ZFTP_CONN|ZFTP_LOGI },
***************
*** 2022,2027 ****
--- 2034,2102 ----
      return zfgetcwd();
  }
  
+ /*
+  * See if the server wants to tell us something.  On a timeout, we usually
+  * have a `421 Timeout' or something such waiting for us, so we read
+  * it here.  As well as being called explicitly by the user
+  * (precmd is a very good place for this, it's cheap since it has
+  * no network overhead), we call it in the bin_zftp front end if we
+  * have a connection and weren't going to call it anyway.
+  *
+  * Poll-free and select-free systems are few and far between these days,
+  * but I'm willing to consider suggestions.
+  */
+ 
+ /**/
+ static int
+ zftp_test(char *name, char **args, int flags)
+ {
+ #if defined(HAVE_POLL) || defined(HAVE_SELECT)
+     int ret;
+ # ifdef HAVE_POLL
+     struct pollfd pfd;
+ # else
+     fd_set f;
+     struct timeval tv;
+ # endif /* HAVE_POLL */
+ 
+     if (zcfd == -1)
+ 	return 1;
+ 
+ # ifdef HAVE_POLL
+ #  ifndef POLLIN
+     /* safety first, though I think POLLIN is more common */
+ #   define POLLIN POLLNORM
+ #  endif /* HAVE_POLL */
+     pfd.fd = zcfd;
+     pfd.events = POLLIN;
+     if ((ret = poll(&pfd, 1, 0)) < 0 && errno != EINTR && errno != EAGAIN)
+ 	zfclose();
+     else if (ret > 0 && pfd.revents) {
+ 	/* handles 421 (maybe a bit noisily?) */
+ 	zfgetmsg();
+     }
+ # else
+     FD_ZERO(&f);
+     FD_SET(zcfd, &f);
+     tv.tv_sec = 0;
+     tv.tv_usec = 0;
+     if ((ret = select(zcfd +1, (SELECT_ARG_2_T) &f, NULL, NULL, &tv)) < 0
+ 	&& errno != EINTR)
+ 	zfclose();
+     else if (ret > 0) {
+ 	/* handles 421 */
+ 	zfgetmsg();
+     }
+ # endif /* HAVE_POLL */
+     /* if we now have zcfd == -1, then we've just been dumped out. */
+     return (zcfd == -1) ? 2 : 0;
+ #else
+     zfwarnnam(name, "not supported on this system.", NULL, 0);
+     return 3;
+ #endif /* defined(HAVE_POLL) || defined(HAVE_SELECT) */
+ }
+ 
+ 
  /* do ls or dir on the remote directory */
  
  /**/
***************
*** 2476,2482 ****
      char fullname[11] = "zftp ";
      char *cnam = *args++, *prefs, *ptr;
      Zftpcmd zptr;
!     int n, ret;
  
      for (zptr = zftpcmdtab; zptr->nam; zptr++)
  	if (!strcmp(zptr->nam, cnam))
--- 2551,2557 ----
      char fullname[11] = "zftp ";
      char *cnam = *args++, *prefs, *ptr;
      Zftpcmd zptr;
!     int n, ret = 0;
  
      for (zptr = zftpcmdtab; zptr->nam; zptr++)
  	if (!strcmp(zptr->nam, cnam))
***************
*** 2521,2528 ****
  				  "B" : "S"), ZFPM_READONLY);
  	}
      }
      if ((zptr->flags & ZFTP_CONN) && zcfd == -1) {
! 	zwarnnam(fullname, "not connected.", NULL, 0);
  	return 1;
      }
  
--- 2596,2620 ----
  				  "B" : "S"), ZFPM_READONLY);
  	}
      }
+ #if defined(HAVE_SELECT) || defined (HAVE_POLL)
+     if (zcfd != -1 && !(zptr->flags & ZFTP_TEST)) {
+ 	/*
+ 	 * Test the connection for a bad fd or incoming message, but
+ 	 * only if the connection was last heard of open, and
+ 	 * if we are not about to call the test command anyway.
+ 	 * Not worth it unless we have select() or poll().
+ 	 */
+ 	ret = zftp_test("zftp test", NULL, 0);
+     }
+ #endif
      if ((zptr->flags & ZFTP_CONN) && zcfd == -1) {
! 	if (ret != 2) {
! 	    /*
! 	     * with ret == 2, we just got dumped out in the test,
! 	     * so enough messages already.
! 	     */	       
! 	    zwarnnam(fullname, "not connected.", NULL, 0);
! 	}
  	return 1;
      }
  
*** configure.in.zftp4	Fri Dec 18 09:47:35 1998
--- configure.in	Fri Dec 18 17:44:11 1998
***************
*** 340,346 ****
  		 termios.h sys/param.h sys/filio.h string.h memory.h \
  		 limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
  		 locale.h errno.h stdlib.h unistd.h sys/capability.h \
! 		 utmp.h utmpx.h sys/types.h pwd.h grp.h)
  if test $dynamic = yes; then
    AC_CHECK_HEADERS(dlfcn.h)
    AC_CHECK_HEADERS(dl.h)
--- 340,346 ----
  		 termios.h sys/param.h sys/filio.h string.h memory.h \
  		 limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
  		 locale.h errno.h stdlib.h unistd.h sys/capability.h \
! 		 utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h)
  if test $dynamic = yes; then
    AC_CHECK_HEADERS(dlfcn.h)
    AC_CHECK_HEADERS(dl.h)
***************
*** 615,621 ****
  dnl AC_FUNC_STRFTIME
  
  AC_CHECK_FUNCS(memcpy memmove \
!               strftime waitpid select tcsetpgrp tcgetattr strstr lstat \
                getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
                sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
                sigprocmask setuid seteuid setreuid setresuid setsid strerror \
--- 615,621 ----
  dnl AC_FUNC_STRFTIME
  
  AC_CHECK_FUNCS(memcpy memmove \
!               strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
                getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
                sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
                sigprocmask setuid seteuid setreuid setresuid setsid strerror \

-- 
Peter Stephenson <pws@xxxxxxxxxxxxxxxxx>       Tel: +39 050 844536
WWW:  http://www.ifh.de/~pws/
Dipartimento di Fisica, Via Buonarroti 2, 56127 Pisa, Italy



Messages sorted by: Reverse Date, Date, Thread, Author