This is the mail archive of the
cygwin-patches@cygwin.com
mailing list for the Cygwin project.
self-upgrading setup.exe
- To: cygwin-patches at cygwin dot com
- Subject: self-upgrading setup.exe
- From: Robert Collins <robert dot collins at itdomain dot com dot au>
- Date: 01 Nov 2001 21:44:26 +1100
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);
}