This is the mail archive of the cygwin-patches@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

self-upgrading setup.exe


This is a proof-of-concept patch.

The concept is to have setup.exe self upgrade - replaceing the current
open file.

When combined with parameters, this can be transparent to the user -
setup can run through to the point it was at before the upgrade need was
detected and carried out.

In the longer term, this is a _significant_ step towards being able to
give setup full dpkg/rpm capabilities _AND_ to allowing setup.exe to
potentially link to cygwin1.dll (via Openlibrary) or use other cygwin
programs as part of it's operation.

Conceptually that requires more than this - it requires dependency
ordered downloads, and some more intelligence that setup has today.


? copyandspawn.cc
? foo.patch
? message
? replaceself.patch
? t.t
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/Makefile.in,v
retrieving revision 2.23
diff -u -p -r2.23 Makefile.in
--- Makefile.in	2001/11/01 01:58:19	2.23
+++ Makefile.in	2001/11/01 10:32:00
@@ -44,7 +44,7 @@ OBJCOPY		:= @OBJCOPY@
 
 include $(srcdir)/../Makefile.common
 
-MINGW_INCLUDES	:= -I. -I$(srcdir) -I$(mingw_source)/include $(w32api_include) -I$(updir)/bz2lib
+MINGW_INCLUDES	:= -I. -I$(srcdir) -I$(mingw_source)/include -I$(w32api_include) -I$(updir)/bz2lib
 
 MINGW_CXXFLAGS	:= -MMD $(CXXFLAGS) -mno-cygwin $(MINGW_INCLUDES) -mwindows
 MINGW_CFLAGS	:= -MMD $(CFLAGS) -mno-cygwin $(MINGW_INCLUDES) -mwindows
@@ -70,9 +70,9 @@ ALL_LDFLAGS	:= ${filter-out -I%, \
 	       ${filter-out -W%, \
 		-B$(w32api_lib)/ -B${mingw_build}/ $(MINGW_CFLAGS) $(LDFLAGS)}}
 
-PROGS	:= setup$(EXEEXT)
+PROGS	:= setup$(EXEEXT) copyandspawn$(EXEEXT)
 
-OBJS = \
+SETUP_OBJS = \
 	autoload.o \
 	choose.o \
 	concat.o \
@@ -115,18 +115,30 @@ OBJS = \
 	version.o \
 	$E
 
+COPYANDSPAWN_OBJS = \
+	copyandspawn.o
+
 .SUFFIXES:
 .NOEXPORT:
 
 .PHONY: all install clean realclean
 
 all: Makefile $(PROGS)
+
+setup$(EXEEXT): $(SETUP_OBJS) $(ALL_DEP_LDLIBS)
+ifdef VERBOSE
+	$(CXX) $(MINGW_CXXFLAGS) -o $@ ${filter-out $(ALL_DEP_LIBS),$^}
+else
+	@echo $(CXX) ... -o $@ $(SETUP_OBJS)
+	@$(CXX) $(MINGW_CXXFLAGS) -o $@ ${filter-out $(ALL_DEP_LIBS),$^} $(ALL_LDFLAGS) $(ALL_LDLIBS)
+endif
+	@chmod a-x $@
 
-setup$(EXEEXT): $(OBJS) $(ALL_DEP_LDLIBS)
+copyandspawn$(EXEEXT): $(COPYANDSPAWN_OBJS) $(ALL_DEP_LDLIBS)
 ifdef VERBOSE
 	$(CXX) $(MINGW_CXXFLAGS) -o $@ ${filter-out $(ALL_DEP_LIBS),$^}
 else
-	@echo $(CXX) ... -o $@ $(OBJS)
+	@echo $(CXX) ... -o $@ $(COPYANDSPAWN_OBJS)
 	@$(CXX) $(MINGW_CXXFLAGS) -o $@ ${filter-out $(ALL_DEP_LIBS),$^} $(ALL_LDFLAGS) $(ALL_LDLIBS)
 endif
 	@chmod a-x $@
Index: choose.cc
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/choose.cc,v
retrieving revision 2.54
diff -u -p -r2.54 choose.cc
--- choose.cc	2001/11/01 08:05:41	2.54
+++ choose.cc	2001/11/01 10:32:01
@@ -924,14 +924,14 @@ _view::insert_under (int linen, pick_lin
   /* part 2 - insert in sorted order in the bucket */
   while (n < nlines)
     {
-      if (lines[n].get_category () || (lines[n].get_pkg ()
-	  && strcasecmp (line.get_pkg ()->name, lines[n].get_pkg ()->name) < 0))
+      if (lines[n].get_pkg () == line.get_pkg ())
 	{
-	  insert_at (n, line);
 	  n = nlines;
 	}
-      else if (lines[n].get_pkg () == line.get_pkg ())
+      else if (lines[n].get_category () || (lines[n].get_pkg ()
+	  && strcasecmp (line.get_pkg ()->name, lines[n].get_pkg ()->name) < 0))
 	{
+	  insert_at (n, line);
 	  n = nlines;
 	}
       n++;
Index: geturl.cc
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/geturl.cc,v
retrieving revision 2.9
diff -u -p -r2.9 geturl.cc
--- geturl.cc	2001/10/31 13:15:05	2.9
+++ geturl.cc	2001/11/01 10:32:02
@@ -172,7 +172,7 @@ progress (int bytes)
   ShowWindow (gw_progress, (max_bytes > 0) ? SW_SHOW : SW_HIDE);
   ShowWindow (gw_pprogress, (total_download_bytes > 0) ? SW_SHOW : SW_HIDE);
   ShowWindow (gw_iprogress, (total_download_bytes > 0) ? SW_SHOW : SW_HIDE);
-  if (max_bytes > 100)
+  if (max_bytes && max_bytes > 100)
     {
       int perc = bytes / (max_bytes / 100);
       SendMessage (gw_progress, PBM_SETPOS, (WPARAM) perc, 0);
Index: ini.cc
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/ini.cc,v
retrieving revision 2.9
diff -u -p -r2.9 ini.cc
--- ini.cc	2001/06/13 16:11:01	2.9
+++ ini.cc	2001/11/01 10:32:02
@@ -18,13 +18,15 @@
    flex parsers are provided also.  We check to see if this setup.ini
    is older than the one we used last time, and if so, warn the user. */
 
-static char *cvsid = "\n%%% $Id: ini.cc,v 2.9 2001/06/13 16:11:01 cgf Exp $\n";
+static char *cvsid =
+  "\n%%% $Id: ini.cc,v 2.9 2001/06/13 16:11:01 cgf Exp $\n";
 
 #include "win32.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <unistd.h>
 
 #include "ini.h"
 #include "resource.h"
@@ -37,6 +39,8 @@ static char *cvsid = "\n%%% $Id: ini.cc,
 #include "log.h"
 #include "version.h"
 #include "mount.h"
+#include "filemanip.h"
+#include "port.h"
 
 unsigned int setup_timestamp = 0;
 char *setup_version = 0;
@@ -68,7 +72,7 @@ do_ini (HINSTANCE h)
   ini_init (ini_file);
 
   setup_timestamp = 0;
-  /*yydebug = 1;*/
+  /*yydebug = 1; */
 
   if (yyparse () || error_count > 0)
     {
@@ -117,21 +121,117 @@ do_ini (HINSTANCE h)
 	}
     }
 
-  msg ("setup_version is %s, our_version is %s", setup_version?:"(null)", version);
+  msg ("setup_version is %s, our_version is %s", setup_version ? : "(null)",
+       version);
   if (setup_version)
     {
       char *ini_version = canonicalize_version (setup_version);
       char *our_version = canonicalize_version (version);
       if (strcmp (our_version, ini_version) < 0)
-	note (IDS_OLD_SETUP_VERSION, version, setup_version);
-    }
+	{
+	  note (IDS_OLD_SETUP_VERSION, version, setup_version);
+	  /* Setup the package can be copied from non-internet
+	   * mirrors, so this test is strictly speaking unneeded,
+	   * but, if someone has updated setup.ini on a local mirror, and 
+	   * NOT performed a download, then not testing this will result in an
+	   * infinite loop
+	   */
+	  if (!strncmp ("ftp://";, MIRROR_SITE, 6) ||
+	      !strncmp ("http://";, MIRROR_SITE, 7)
+	      /* for testing */
+	      || 1)
+	    {
+	      /* we need copyandspawn.exe. Don't worry about versioning/size...
+	       * That will get fixed when copyandspawn is listed as a package
+	       */
+	      char *local = "latest/setup/copyandspawn.exe";
+	      if (get_file_size (local) == 0)
+		{
+
+		  mkdir_p (0, local);
+
+		  if (get_url_to_file (concat (MIRROR_SITE, "/", local, 0),
+				       concat (local, ".tmp", 0), 0))
+		    {
+		      note (IDS_DOWNLOAD_FAILED, local);
+		      goto finished;
+		    }
+		  else
+		    {
+		      log (0, "Downloaded %s", local);
+		      if (_access (local, 0) == 0)
+			remove (local);
+		      rename (concat (local, ".tmp", 0), local);
+		    }
+		}
+
+	      /* download the new setup.exe as setup.new */
+	      local = "latest/setup/setup.exe";
+	      /* file size is close enough to unique */
+	      if (get_file_size (local) != get_file_size ("setup.exe"))
+		{
+
+		  mkdir_p (0, local);
+
+		  if (get_url_to_file (concat (MIRROR_SITE, "/", local, 0),
+				       concat (local, ".tmp", 0), 0))
+		    {
+		      note (IDS_DOWNLOAD_FAILED, local);
+		      goto finished;
+		    }
+		  else
+		    {
+		      log (0, "Downloaded %s", local);
+		      if (_access (local, 0) == 0)
+			remove (local);
+		      rename (concat (local, ".tmp", 0), "latest/setup/setup.new");
+		    }
+		}
+
+	      /* max len = pathlen to copyandspawn + 4 + pathlen to us +
+	       * pathlen to setup.new + pathlen to
+	       * us + pid in str form.
+	       */
+	      char *program =
+		(char *) alloca (strlen (local_dir) +
+				 strlen ("copyandspawn.exe") + 2);
+	      sprintf (program, "%s\\%s", local_dir, "copyandspawn.exe");
+	      char *cmdline =
+		(char *) alloca (strlen (program) + 3 + strlen (myself) * 2 +
+				 strlen (local_dir) + 9 + 20);
+	      sprintf (cmdline, "%s %ul %s\\%s %s %s", program,
+		       GetCurrentProcessId (),
+		       local_dir, "setup.new", myself, myself);
+	      
+	      /* replace this program */
+	      STARTUPINFO si;
+	      PROCESS_INFORMATION pi;
+	      ZeroMemory (&si, sizeof (si));
+	      si.cb = sizeof (si);
+	      ZeroMemory (&pi, sizeof (pi));
+
+	      if (CreateProcess (program,
+				 cmdline,
+				 NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+		{
+		  /* Successfully launched the process */
+		  CloseHandle (pi.hProcess);
+		  CloseHandle (pi.hThread);
+		  log (LOG_TIMESTAMP, "launched copyandspawn successfully");
+		  exit_setup (0);
+		}
+	    }
+	}
 
+    }
+finished:
   next_dialog = IDD_CHOOSE;
 }
 
 extern int yylineno;
 
-extern "C" int yyerror (char *s, ...)
+extern "C" int
+yyerror (char *s, ...)
 {
   char buf[1000];
   int len;
@@ -156,12 +256,12 @@ extern "C" int yyerror (char *s, ...)
   error_count++;
 }
 
-extern "C" int fprintf (FILE *f, const char *s, ...);
+extern "C" int fprintf (FILE * f, const char *s, ...);
 
 static char stderrbuf[1000];
 
 int
-fprintf (FILE *f, const char *fmt, ...)
+fprintf (FILE * f, const char *fmt, ...)
 {
   char buf[1000];
   int rv;
@@ -174,7 +274,7 @@ fprintf (FILE *f, const char *fmt, ...)
       if (char *nl = strchr (stderrbuf, '\n'))
 	{
 	  *nl = 0;
-	  /*OutputDebugString (stderrbuf);*/
+	  /*OutputDebugString (stderrbuf); */
 	  MessageBox (0, buf, "Cygwin Setup", 0);
 	  stderrbuf[0] = 0;
 	}
Index: main.cc
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/main.cc,v
retrieving revision 2.8
diff -u -p -r2.8 main.cc
--- main.cc	2001/11/01 01:58:19	2.8
+++ main.cc	2001/11/01 10:32:02
@@ -144,6 +144,8 @@ WinMain (HINSTANCE h,
   for (argc = 0, argv = __argv; *argv; argv++)
     log (LOG_TIMESTAMP, "%d - '%s'\n", argc++, *argv);
   log (LOG_TIMESTAMP, "%d parameters passed\n", argc);
+  argv = __argv;
+  myself = strdup (argv[0]);
   
   /* Set the default DACL only on NT/W2K. 9x/ME has no idea of access
      control lists and security at all. */
Index: res.rc
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/res.rc,v
retrieving revision 2.27
diff -u -p -r2.27 res.rc
--- res.rc	2001/10/27 16:20:45	2.27
+++ res.rc	2001/11/01 10:32:02
@@ -405,7 +405,7 @@ BEGIN
     IDS_UNINSTALL_COMPLETE  "Uninstalls complete."
     IDS_WININET             "Unable to find or load the Internet Explorer 5 DLLs"
     IDS_ERR_CHDIR           "Could not change dir to %s"
-    IDS_OLD_SETUP_VERSION   "This setup is version %s, but setup.ini claims version %s is available.\nYou might want to upgrade to get the latest features and bug fixes."
+    IDS_OLD_SETUP_VERSION   "This setup is version %s, but setup.ini claims version %s is available.\nIf you chose to download from the Internet, setup will now upgrade itself.\nIf you are installing from a local mirror you might want to upgrade to\nget the latest features and bug fixes."
     IDS_DOWNLOAD_FAILED     "Unable to download %s"
     IDS_DOWNLOAD_INCOMPLETE "Download Incomplete.  Try again?"
     IDS_INSTALL_INCOMPLETE  "Installation incomplete.  Check /setup.log.full for details"
Index: state.h
===================================================================
RCS file: /cvs/src/src/winsup/cinstall/state.h,v
retrieving revision 2.4
diff -u -p -r2.4 state.h
--- state.h	2001/05/28 08:31:02	2.4
+++ state.h	2001/11/01 10:32:02
@@ -21,6 +21,7 @@
 extern int	source;
 
 extern char *	local_dir;
+extern char *   myself;
 
 extern int	root_text;
 extern int	root_scope;
/*
 * Copyright (c) 2001, Robert Collins
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     A copy of the GNU General Public License can be found at
 *     http://www.gnu.org/
 *
 * Written by R B Collins <rbtcollins@hotmail.com>
 *
 */

/* What this does:
 *
 * Now:
 *
 * Given a windows PID, a replacement file name, a destination file name, 
 * a program (full path recommended) it
 * Waits for that windows PID to terminate.
 * Moves the replacement file over the destination file.
 * Launches the program.
 * Quits.
 * 
 * In the future:
 *
 * >No PID needed, instead use a system state snapshot or equivalent to
 * identify what currently running programs have the destination file open.
 * >Present a dialogue to the user if there are programs holding the file open
 * listing those programs.
 * >Offer to kill those programs.
 * >Accept optional command line arguments to be passed to the program when it
 * is spawned.
 *
 * And still further:
 * > Allow a group of related files to be specified, ie, foo1->bar1, foo2->bar2
 * ...
 */

static char *cvsid = "\n%%% $Id$\n";

#include "win32.h"

#include <stdio.h>
#include <stdlib.h>

#define iswinnt		(GetVersion() < 0x80000000)
#define FAST_FUNCTION  __attribute__ ((stdcall, regparm(2)))

static void
usage (void)
{
	
}

int WINAPI
WinMain (HINSTANCE h,
	 HINSTANCE hPrevInstance,
	 LPSTR command_line,
	 int cmd_show)
{
  char ** argv;
  int argc;
  char cwd[_MAX_PATH];
  GetCurrentDirectory (sizeof (cwd), cwd);

  argc=__argc;
  argv=__argv;

  FILE *flog = fopen ("copyandspawn.log", "a");
  
  if  (__argc != 5 )
    {
      if (flog)
	{
	  fprintf (flog, "not enough parameters\n");
          fflush (flog);
	  fclose (flog);
	}
      ExitProcess (1); 
    }

  long winpid=0;

  winpid = strtol (argv[1], NULL, 10);
  if (errno || winpid ==0)
    ExitProcess (1);
  HANDLE hProcess = OpenProcess (SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
		  FALSE,
		  winpid);
  if (hProcess)
    {
      /* Wait for the process to quit */
      if (WaitForSingleObject (hProcess, INFINITE) == WAIT_FAILED)
//	ExitProcess (1);
//	probably it closed before we got going.
      CloseHandle (hProcess);
    }

  /* Delete the target file - work around win 9x bugs */
  if (DeleteFile (argv[3]))
    {
      /* Failed to delete the file 
       * This may be because the file doesn't exist, so just try the Move
       */
    }
	  
  /* Move the replacement over the target */
  if (MoveFile (argv[2], argv[3]))
    {
      /* the move failed. */
    }

  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  ZeroMemory( &si, sizeof(si) );
  si.cb = sizeof(si);
  ZeroMemory( &pi, sizeof(pi) );
	  
  
  /* Now launch the program that was requested */
  if (!CreateProcess (argv[4], NULL, NULL, NULL, 0,
		     NORMAL_PRIORITY_CLASS, NULL, 
		     /* CWD - may need setting */ NULL,
		     &si, &pi))
    {
      /* Aww, shucks, we can't run the program we were asked too. At this
       * point we really should open a window to tell the user...
       */
      
      if (flog)
	{
          fprintf (flog, "failed to spawn process %s", argv[4]);
	  fflush (flog);
	  fclose (flog);
	}
    }
  else
    {
      CloseHandle( pi.hProcess );
      CloseHandle( pi.hThread );
    }
  
  ExitProcess (0);
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]