Subject: Fully working cvs client/server on Windows NT
From: [email-masked] (Andy Piper)

18 Nov 1997 07:43:24 -0800
Message-ID: <3.0.1.32.19971118150928.00b36330.cygnus.gnu-win32@mailhost>
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
To: [email-masked], [email-masked]

Below is a patch and description of how to get cvs to work in pserver
mode on windows NT. The patch not only enables pserver to work but
fixes a couple of bugs to do with binary file handling on windows NT.
Note that these fixes are relevant to UNIX servers hosting NT clients
as well as NT servers. In particular the binary mode fix is a server
fix. The patch does not affect client operation so I would tend to
advise that people use the prebuilt 1.9.10 as the client - although
1.9.18 has client-side improvements that people may want to avail
themselves of. That said both client & server rely on cygwin32 for
pathname handling, so there may be client-side issues to do with this.

To build you need:
	cygwin32 development kit b18 from 
		http://www.cygnus.com/misc/gnu-win32/
	Sergey O's latest cygwin32.dll & libcygwin.a from 
		http://miracle.geol.msu.ru/sos/
	libcrypt.a & crypt.exe from Sergey O's website. crypt.exe
		allows you to generate an md5 type password that 
		you can put in CVSROOT/passwd.
	cvs-1.9.18.tar.gz

To build:

-) unpack the cvs distribution.
-) apply the patch in the src directory
-) ./configure --enable-server=yes --enable-client=yes
-) make

To start the server on NT listening on 2401:

cvs --allow-root=whatever pserverd

The patches are relevant to a native NT build but probably need some
modification and I don't have the time or the inclination. The only
outstanding binary mode bug that I know of is that cvs admin -kb
doesn't register the fact on the client side, but this is fixed
whenever you do an update so I can't get too excited about it. 

I've probably missed something, but have fun :)

andy

*** filesubr.c	1997/11/18 11:52:09	1.1
--- filesubr.c	1997/11/18 12:01:51
***************
*** 418,427 ****
      const char *f;
  {
      if (trace)
! #ifdef SERVER_SUPPORT
! 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
! 			(server_active) ? 'S' : ' ', f);
! #else
  	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
  #endif
      if (noexec)
--- 418,424 ----
      const char *f;
  {
      if (trace)
! #ifndef SERVER_SUPPORT
  	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
  #endif
      if (noexec)
***************
*** 876,878 ****
--- 873,905 ----
      return retval;
  }
  #endif /* SERVER_SUPPORT */
+ 
+ #ifdef LINES_CRLF_TERMINATED
+ /* Read data from INFILE, and copy it to OUTFILE. 
+    Open INFILE using INFLAGS, and OUTFILE using OUTFLAGS.
+    This is useful for converting between CRLF and LF line formats.  */
+ void
+ convert_file (char *infile,  int inflags,
+ 	      char *outfile, int outflags)
+ {
+     int infd, outfd;
+     char buf[8192];
+     int len;
+ 
+     if ((infd = open (infile, inflags)) < 0)
+         error (1, errno, "couldn't read %s", infile);
+     if ((outfd = open (outfile, outflags, S_IWRITE)) < 0)
+         error (1, errno, "couldn't write %s", outfile);
+ 
+     while ((len = read (infd, buf, sizeof (buf))) > 0)
+         if (write (outfd, buf, len) < 0)
+ 	    error (1, errno, "error writing %s", outfile);
+     if (len < 0)
+         error (1, errno, "error reading %s", infile);
+ 
+     if (close (outfd) < 0)
+         error (0, errno, "warning: couldn't close %s", outfile);
+     if (close (infd) < 0)
+         error (0, errno, "warning: couldn't close %s", infile);
+ }
+ #endif
*** main.c	1997/10/31 09:31:28	1.1
--- main.c	1997/11/18 13:12:54
***************
*** 20,25 ****
--- 20,30 ----
  extern int gethostname ();
  #endif
  
+ #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #endif
+ 
  char *program_name;
  char *program_path;
  char *command_name;
***************
*** 107,112 ****
--- 112,118 ----
      { "logout",   NULL,       NULL,        logout },
  #ifdef SERVER_SUPPORT
      { "pserver",  NULL,       NULL,        server }, /* placeholder */
+     { "pserverd", NULL,       NULL,        server }, /* placeholder */
  #endif
  #endif /* AUTH_CLIENT_SUPPORT */
      { "rdiff",    "patch",    "pa",        patch },
***************
*** 365,370 ****
--- 371,391 ----
  #endif /* !DONT_USE_SIGNALS */
  }
  
+ #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
+ void reap_child(signo)
+      int signo;
+ {
+   int status;
+   pid_t pid;
+   /* wait for our process to die and munge return status */
+ #ifdef HAVE_WAIT3
+   pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ #else
+   pid = wait (&status);
+ #endif
+ }
+ #endif
+ 
  int
  main (argc, argv)
      int argc;
***************
*** 380,386 ****
      int free_CVSroot = 0;
      int free_Editor = 0;
      int free_Tmpdir = 0;
! 
      int help = 0;		/* Has the user asked for help?  This
  				   lets us support the `cvs -H cmd'
  				   convention to give help for cmd. */
--- 401,420 ----
      int free_CVSroot = 0;
      int free_Editor = 0;
      int free_Tmpdir = 0;
! #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
!     int		servsock, childsock;
!     struct sockaddr_in server;
!     int on = 1;
! #ifdef POSIX_SIGNALS
!     struct sigaction act;
! #else
! #ifdef BSD_SIGNALS
!     struct sigvec vec;
! #else
!     RETSIGTYPE (*istat) ();
! #endif
! #endif
! #endif
      int help = 0;		/* Has the user asked for help?  This
  				   lets us support the `cvs -H cmd'
  				   convention to give help for cmd. */
***************
*** 682,687 ****
--- 716,834 ----
        
  	    /* Pretend we were invoked as a plain server.  */
  	    command_name = "server";
+ 	}
+ 
+ 	if (strcmp (command_name, "pserverd") == 0)
+ 	{
+ 	  /* pserver daemon mode */
+ 	  /* first fork and exit so that the child takes control */
+ 	  if (!trace && fork())
+ 	    {
+ 	      exit(0);
+ 	    }
+ 	  /* become a daemon */
+ 	  if (!trace && setsid() < 0)
+ 	    {
+ 	      error(1, errno, "pserverd: setsid failed");
+ 	    }
+ 
+ 	  /* create a socket to listen on */
+ 	  if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) <0)
+ 	    {
+ 	      error(1, errno, "pserverd: socket() failed");
+ 	    }
+ 	  setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+ 		     sizeof(on));
+ 
+ 	  server.sin_family = AF_INET;
+ 	  server.sin_addr.s_addr = INADDR_ANY;
+ 	  server.sin_port = htons(CVS_AUTH_PORT);
+ 
+ 	  if (bind(servsock, (struct sockaddr *)&server, sizeof(server)) < 0)
+ 	    {
+ 	      shutdown(servsock, 2);
+ 	      close(servsock);
+ 	      error(1, errno, "pserverd: bind failed");
+ 	    }
+ 
+ 	  if (listen(servsock, 5) < 0)
+ 	    {
+ 	      error(1, errno, "pserverd: listen failed");
+ 	    }
+ 	  
+ 	  for (;;) {
+ 	      if ((childsock = 
+ 		   accept(servsock, (struct sockaddr *)0, (int*)0))<0)
+ 		  if (errno==EINTR)
+ 		      {
+ 			  continue;
+ 		      }
+ 		  else
+ 		      {
+ 			  error(1, errno, "pserverd: accept() failed");
+ 		      }
+ 	  
+ 	    if (trace || !fork())
+ 	      {			/* child */
+ 		close(servsock);
+ 		if (dup2(childsock,STDIN_FILENO)<0)
+ 		  {
+ 		    error(1, errno, "pserverd: dup2(stdin) failed");
+ 		  }
+ 		if ( close(childsock)<0 )
+ 		  {
+ 		    error(1, errno, "pserverd: close(childsock) failed");
+ 		  }
+ 		if (dup2(STDIN_FILENO,STDOUT_FILENO)<0)
+ 		  {
+ 		    error(1, errno, "pserverd: dup2(stdout) failed");
+ 		  }
+ 		if (dup2(STDIN_FILENO,STDERR_FILENO)<0)
+ 		  {
+ 		    error(1, errno, "pserverd: dup2(stderr) failed");
+ 		  }
+ #ifdef POSIX_SIGNALS
+ 		act.sa_handler = SIG_DFL;
+ 		(void) sigemptyset (&act.sa_mask);
+ 		act.sa_flags = 0;
+ 		(void) sigaction (SIGCHLD, &act, (struct sigaction*)0);
+ #else
+ #ifdef BSD_SIGNALS
+ 		memset ((char *) &vec, 0, sizeof (vec));
+ 		vec.sv_handler = SIG_DFL;
+ 		(void) sigvec (SIGCHLD, &vec, (struct sigvec*)0);
+ #else
+ 		istat = signal (SIGCHLD, SIG_DFL);
+ #endif
+ #endif
+ 		break;
+ 	      }
+ 	    else
+ 	      {
+ 		if (!trace)
+ 		  {
+ #ifdef POSIX_SIGNALS
+ 		    act.sa_handler = reap_child;
+ 		    (void) sigemptyset (&act.sa_mask);
+ 		    act.sa_flags = 0;
+ 		    (void) sigaction (SIGCHLD, &act, (struct sigaction*)0);
+ #else
+ #ifdef BSD_SIGNALS
+ 		    memset ((char *) &vec, 0, sizeof (vec));
+ 		    vec.sv_handler = reap_child;
+ 		    (void) sigvec (SIGCHLD, &vec, (struct sigvec*)0);
+ #else
+ 		    istat = signal (SIGCHLD, reap_child);
+ #endif
+ #endif
+ 		  }
+ 		close(childsock);
+ 	      }
+ 	  }
+ 
+ 	  pserver_authenticate_connection ();
+ 	  /* Pretend we were invoked as a plain server.  */
+ 	  command_name = "server";
  	}
  #endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
  
*** options.h.in	1997/11/18 13:52:10	1.1
--- options.h.in	1997/11/18 14:00:31
***************
*** 252,260 ****
--- 252,267 ----
   * start again.  You may override the default hi/low watermarks here
   * too.
   */
+ 
+ #ifndef __CYGWIN32__
  #define SERVER_FLOWCONTROL
  #define SERVER_HI_WATER (2 * 1024 * 1024)
  #define SERVER_LO_WATER (1 * 1024 * 1024)
+ #endif
+ 
+ #ifdef __CYGWIN32__
+ #define LINES_CR_LF_TERMINATED 1
+ #endif
  
  /* End of CVS configuration section */
  
*** server.c	1997/11/12 10:23:57	1.1
--- server.c	1997/11/12 10:24:39
***************
*** 4364,4369 ****
--- 4364,4370 ----
      initgroups (pw->pw_name, pw->pw_gid);
  #endif /* HAVE_INITGROUPS */
  
+ #ifndef __CYGWIN32__
  #ifdef SETXID_SUPPORT
      /* honor the setgid bit iff set*/
      if (getgid() != getegid())
***************
*** 4381,4386 ****
--- 4382,4388 ----
      /* We don't want our umask to change file modes.  The modes should
         be set by the modes used in the repository, and by the umask of
         the client.  */
+ #endif
      umask (0);
  
  #if HAVE_PUTENV
*** vers_ts.c	1997/11/18 10:24:16	1.1
--- vers_ts.c	1997/11/18 10:38:32
***************
*** 100,110 ****
  
      /*
       * -k options specified on the command line override (and overwrite)
!      * options stored in the entries file
       */
!     if (options)
  	vers_ts->options = xstrdup (options);
!     else if (!vers_ts->options)
      {
  	if (finfo->rcs != NULL)
  	{
--- 100,111 ----
  
      /*
       * -k options specified on the command line override (and overwrite)
!      * options stored in the entries file. However, options is always set
!      * (even if only a null string) so need to check for emptiness as well.
       */
!     if (options && *options != '\0')
  	vers_ts->options = xstrdup (options);
!     else if (!vers_ts->options || *vers_ts->options == '\0')
      {
  	if (finfo->rcs != NULL)
  	{

   ___                ____         	Dr Andy Piper
  / _ \___ ________ _/ / Solutions_	(require 'disclaimer)
 / ___/ _ `/ __/ _ `/ / / _ `/\ \ /	[email-masked]
/_/   \_,_/_/  \_,_/_/_/\_,_//_\_\ 	boot /vmemacs

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".