summaryrefslogtreecommitdiffstats
path: root/PC
diff options
context:
space:
mode:
authorMark Hammond <mhammond@skippinet.com.au>2008-05-02 12:48:15 (GMT)
committerMark Hammond <mhammond@skippinet.com.au>2008-05-02 12:48:15 (GMT)
commit7c5c8e6823e476b46a30373e5a95e8b8e4c5fa09 (patch)
tree281a15194c99882fa645183f76aa5581ba57d9d2 /PC
parentf8cc64017ca0f33b1e21b1c9c9f5f30ebdc4da31 (diff)
downloadcpython-7c5c8e6823e476b46a30373e5a95e8b8e4c5fa09.zip
cpython-7c5c8e6823e476b46a30373e5a95e8b8e4c5fa09.tar.gz
cpython-7c5c8e6823e476b46a30373e5a95e8b8e4c5fa09.tar.bz2
#2581: Vista UAC/elevation support for bdist_wininst
Diffstat (limited to 'PC')
-rw-r--r--PC/bdist_wininst/install.c110
-rw-r--r--PC/bdist_wininst/wininst-7.1.vcproj4
-rw-r--r--PC/bdist_wininst/wininst.dsp6
3 files changed, 112 insertions, 8 deletions
diff --git a/PC/bdist_wininst/install.c b/PC/bdist_wininst/install.c
index 0ce2371..a4f4865 100644
--- a/PC/bdist_wininst/install.c
+++ b/PC/bdist_wininst/install.c
@@ -133,6 +133,7 @@ char meta_name[80]; /* package name without version like
char install_script[MAX_PATH];
char *pre_install_script; /* run before we install a single file */
+char user_access_control[10]; // one of 'auto', 'force', otherwise none.
int py_major, py_minor; /* Python version selected for installation */
@@ -344,8 +345,15 @@ struct PyMethodDef {
};
typedef struct PyMethodDef PyMethodDef;
+// XXX - all of these are potentially fragile! We load and unload
+// the Python DLL multiple times - so storing functions pointers
+// is dangerous (although things *look* OK at present)
+// Better might be to roll prepare_script_environment() into
+// LoadPythonDll(), and create a new UnloadPythonDLL() which also
+// clears the global pointers.
void *(*g_Py_BuildValue)(char *, ...);
int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
+PyObject * (*g_PyLong_FromVoidPtr)(void *);
PyObject *g_PyExc_ValueError;
PyObject *g_PyExc_OSError;
@@ -597,7 +605,7 @@ static PyObject *PyMessageBox(PyObject *self, PyObject *args)
static PyObject *GetRootHKey(PyObject *self)
{
- return g_Py_BuildValue("l", hkey_root);
+ return g_PyLong_FromVoidPtr(hkey_root);
}
#define METH_VARARGS 0x0001
@@ -631,7 +639,9 @@ static HINSTANCE LoadPythonDll(char *fname)
"SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
py_major, py_minor);
if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
- fullpath, &size))
+ fullpath, &size) &&
+ ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
+ fullpath, &size))
return NULL;
strcat(fullpath, "\\");
strcat(fullpath, fname);
@@ -648,6 +658,7 @@ static int prepare_script_environment(HINSTANCE hPython)
DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
+ DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
if (!PyImport_ImportModule || !PyObject_GetAttrString ||
!PyObject_SetAttrString || !PyCFunction_New)
return 1;
@@ -667,6 +678,7 @@ static int prepare_script_environment(HINSTANCE hPython)
g_Py_BuildValue = Py_BuildValue;
g_PyArg_ParseTuple = PyArg_ParseTuple;
g_PyErr_Format = PyErr_Format;
+ g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
return 0;
}
@@ -777,7 +789,9 @@ static int run_simple_script(char *script)
hPython = LoadPythonDll(pythondll);
if (!hPython) {
- set_failure_reason("Can't load Python for pre-install script");
+ char reason[128];
+ wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
+ set_failure_reason(reason);
return -1;
}
rc = do_run_simple_script(hPython, script);
@@ -2073,6 +2087,71 @@ void RunWizard(HWND hwnd)
PropertySheet(&psh);
}
+// subtly different from HasLocalMachinePrivs(), in that after executing
+// an 'elevated' process, we expect this to return TRUE - but there is no
+// such implication for HasLocalMachinePrivs
+BOOL MyIsUserAnAdmin()
+{
+ typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
+ static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
+ HMODULE shell32;
+ // This function isn't guaranteed to be available (and it can't hurt
+ // to leave the library loaded)
+ if (0 == (shell32=LoadLibrary("shell32.dll")))
+ return FALSE;
+ if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
+ return FALSE;
+ return (*pfnIsUserAnAdmin)();
+}
+
+// Some magic for Vista's UAC. If there is a target_version, and
+// if that target version is installed in the registry under
+// HKLM, and we are not current administrator, then
+// re-execute ourselves requesting elevation.
+// Split into 2 functions - "should we elevate" and "spawn elevated"
+
+// Returns TRUE if we should spawn an elevated child
+BOOL NeedAutoUAC()
+{
+ HKEY hk;
+ char key_name[80];
+ OSVERSIONINFO winverinfo;
+ winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
+ // If less than XP, then we can't do it (and its not necessary).
+ if (!GetVersionEx(&winverinfo) || winverinfo.dwMajorVersion < 5)
+ return FALSE;
+ // no Python version info == we can't know yet.
+ if (target_version[0] == '\0')
+ return FALSE;
+ // see how python is current installed
+ wsprintf(key_name,
+ "Software\\Python\\PythonCore\\%s\\InstallPath",
+ target_version);
+ if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key_name, 0, KEY_READ, &hk))
+ return FALSE;
+ RegCloseKey(hk);
+ // Python is installed in HKLM - we must elevate.
+ return TRUE;
+}
+
+// Spawn ourself as an elevated application. On failure, a message is
+// displayed to the user - but this app will always terminate, even
+// on error.
+void SpawnUAC()
+{
+ // interesting failure scenario that has been seen: initial executable
+ // runs from a network drive - but once elevated, that network share
+ // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
+ int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
+ SW_SHOWNORMAL);
+ if (ret <= 32) {
+ char msg[128];
+ wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
+ MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
+ }
+}
+
int DoInstall(void)
{
char ini_buffer[4096];
@@ -2106,6 +2185,31 @@ int DoInstall(void)
install_script, sizeof(install_script),
ini_file);
+ GetPrivateProfileString("Setup", "user_access_control", "",
+ user_access_control, sizeof(user_access_control), ini_file);
+
+ // See if we need to do the Vista UAC magic.
+ if (strcmp(user_access_control, "force")==0) {
+ if (!MyIsUserAnAdmin()) {
+ SpawnUAC();
+ return 0;
+ }
+ // already admin - keep going
+ } else if (strcmp(user_access_control, "auto")==0) {
+ // Check if it looks like we need UAC control, based
+ // on how Python itself was installed.
+ if (!MyIsUserAnAdmin() && NeedAutoUAC()) {
+ SpawnUAC();
+ return 0;
+ }
+ } else {
+ // display a warning about unknown values - only the developer
+ // of the extension will see it (until they fix it!)
+ if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
+ MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
+ // nothing to do.
+ }
+ }
hwndMain = CreateBackground(title);
diff --git a/PC/bdist_wininst/wininst-7.1.vcproj b/PC/bdist_wininst/wininst-7.1.vcproj
index b73cf34..1ce2bf1 100644
--- a/PC/bdist_wininst/wininst-7.1.vcproj
+++ b/PC/bdist_wininst/wininst-7.1.vcproj
@@ -24,7 +24,7 @@
Name="VCCLCompilerTool"
Optimization="1"
InlineFunctionExpansion="1"
- AdditionalIncludeDirectories="..\..\Include,..\..\..\zlib-1.2.1"
+ AdditionalIncludeDirectories="..\..\Include,..\..\..\zlib-1.2.3"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
StringPooling="TRUE"
RuntimeLibrary="2"
@@ -41,7 +41,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="..\..\..\zlib-1.2.1\zlib.lib imagehlp.lib comctl32.lib"
+ AdditionalDependencies="..\..\..\zlib-1.2.3\zlib.lib imagehlp.lib comctl32.lib"
OutputFile="..\..\lib\distutils\command/wininst-7.1.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
diff --git a/PC/bdist_wininst/wininst.dsp b/PC/bdist_wininst/wininst.dsp
index 23a6591..38be55a 100644
--- a/PC/bdist_wininst/wininst.dsp
+++ b/PC/bdist_wininst/wininst.dsp
@@ -43,7 +43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /O1 /I "..\..\Include" /I "..\..\..\zlib-1.2.1" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /O1 /I "..\..\Include" /I "..\..\..\zlib-1.2.3" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x407 /d "NDEBUG"
@@ -53,7 +53,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
-# ADD LINK32 ..\..\..\zlib-1.2.1\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\lib\distutils\command/wininst-6.exe"
+# ADD LINK32 ..\..\..\zlib-1.2.3\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\lib\distutils\command/wininst-6.0.exe"
!ELSEIF "$(CFG)" == "wininst - Win32 Debug"
@@ -79,7 +79,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 ..\..\..\zlib-1.2.1\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\lib\distutils\command/wininst-6_d.exe"
+# ADD LINK32 ..\..\..\zlib-1.2.3\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"LIBC" /out:"..\..\lib\distutils\command/wininst-6.0_d.exe"
!ENDIF