Save the registry on server exit without client intervention.

Removed "alt" registry files since we now have symlinks and
WINEPREFIX to replace them.
This commit is contained in:
Alexandre Julliard 2000-04-16 17:21:13 +00:00
parent fa8b7281cd
commit c970904c2c
10 changed files with 253 additions and 241 deletions

View File

@ -270,26 +270,6 @@ Use Win95-like window displays or Win3.1-like window displays.
.PP
.B [Registry]
.br
.I format: AltCurrentUserFile=<filename>
.br
alternate registry file name: HKEY_CURRENT_USER
.PP
.I format: AltUserFile=<filename>
.br
alternate registry file name: HKKEY_USERS
.PP
.I format: AltLocalMachineFile=<filename>
.br
alternate registry file name: HKEY_LOCAL_MASCHINE
.PP
.I format: LoadAltRegistryFiles=<boolean>
.br
Load above registries.
.PP
.I format: WritetoAltRegistryFiles=<boolean>
.br
TRY to write all changes to alt registries
.PP
.I format: LoadGlobalRegistryFiles=<boolean>
.br
Global registries (stored in /etc)
@ -308,9 +288,7 @@ Load Windows registry from the current Windows directory.
.PP
booleans: Y/y/T/t/1 are true, N/n/F/f/0 are false.
.br
Defaults are read all, write to Home and Alt
.PP
Note: it is pointless to specify alt files and neither load nor write to them.
Defaults are read all, write to Home
.PP
.SH SAMPLE CONFIGURATION FILE
A sample configuration file is distributed as

View File

@ -996,12 +996,21 @@ struct save_registry_request
};
/* Save a registry branch at server exit */
struct save_registry_atexit_request
{
IN int hkey; /* key to save */
IN char file[1]; /* file to save to */
};
/* Set the current and saving level for the registry */
struct set_registry_levels_request
{
IN int current; /* new current level */
IN int saving; /* new saving level */
IN int version; /* file format version for saving */
IN int period; /* duration between periodic saves (milliseconds) */
};
@ -1194,6 +1203,7 @@ enum request
REQ_DELETE_KEY_VALUE,
REQ_LOAD_REGISTRY,
REQ_SAVE_REGISTRY,
REQ_SAVE_REGISTRY_ATEXIT,
REQ_SET_REGISTRY_LEVELS,
REQ_CREATE_TIMER,
REQ_OPEN_TIMER,
@ -1209,7 +1219,7 @@ enum request
REQ_NB_REQUESTS
};
#define SERVER_PROTOCOL_VERSION 7
#define SERVER_PROTOCOL_VERSION 8
/* ### make_requests end ### */
/* Everything above this line is generated automatically by tools/make_requests */

View File

@ -14,8 +14,6 @@ extern "C" {
* shell 16
*/
extern void SHELL_LoadRegistry(void);
extern void SHELL_SaveRegistry(void);
extern void SHELL_InitRegistrySaving(void);
/* global functions used from shell32 */
extern HINSTANCE SHELL_FindExecutable(LPCSTR,LPCSTR ,LPSTR);

View File

@ -97,8 +97,6 @@ BOOL MAIN_MainInit( int argc, char *argv[], BOOL win32 )
if (!LoadLibraryA( "x11drv" )) return FALSE;
SHELL_InitRegistrySaving();
return TRUE;
}
@ -180,8 +178,6 @@ void WINAPI ExitKernel16( void )
/* Do the clean-up stuff */
WriteOutProfiles16();
SHELL_SaveRegistry();
TerminateProcess( GetCurrentProcess(), 0 );
}

View File

@ -149,155 +149,6 @@ static void REGISTRY_Init(void) {
}
/************************ SAVE Registry Function ****************************/
#define REGISTRY_SAVE_VERSION 0x00000001
/* Registry saveformat:
* If you change it, increase above number by 1, which will flush
* old registry database files.
*
* Global:
* "WINE REGISTRY Version %d"
* subkeys....
* Subkeys:
* keyname
* valuename=lastmodified,type,data
* ...
* subkeys
* ...
* keyname,valuename,stringdata:
* the usual ascii characters from 0x00-0xff (well, not 0x00)
* and \uXXXX as UNICODE value XXXX with XXXX>0xff
* ( "=\\\t" escaped in \uXXXX form.)
* type,lastmodified:
* int
*
* FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
*
* [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
* SaveOnlyUpdatedKeys=yes
*/
/* Same as RegSaveKey but with Unix pathnames */
static void save_key( HKEY hkey, const char *filename )
{
struct save_registry_request *req = get_req_buffer();
int count = 0;
DWORD ret;
HANDLE handle;
char *p;
char *rname = HeapAlloc( GetProcessHeap(), 0, PATH_MAX );
char *name;
/* use realpath to resolve any symlinks
* I assume that rname is filled in correctly if the error is ENOENT */
if ((realpath(filename, rname) == NULL) && (errno != ENOENT))
{
ERR( "Failed to find real path of %s: ", filename );
perror( "realpath" );
HeapFree( GetProcessHeap(), 0, rname );
return;
}
name = HeapAlloc( GetProcessHeap(), 0, strlen(rname) + 20 );
if (!name) return;
strcpy( name, rname );
if ((p = strrchr( name, '/' ))) p++;
else p = name;
for (;;)
{
sprintf( p, "reg%04x.tmp", count++ );
handle = FILE_CreateFile( name, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, -1, TRUE );
if (handle != INVALID_HANDLE_VALUE) break;
if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) break;
}
if (handle != INVALID_HANDLE_VALUE)
{
req->hkey = hkey;
req->file = handle;
ret = server_call_noerr( REQ_SAVE_REGISTRY );
CloseHandle( handle );
if (ret) unlink( name );
else if (rename( name, rname ) == -1)
{
ERR( "Failed to move %s to %s: ", name, rname );
perror( "rename" );
unlink( name );
}
}
else ERR( "Failed to save registry to %s, err %ld\n", name, GetLastError() );
HeapFree( GetProcessHeap(), 0, rname );
HeapFree( GetProcessHeap(), 0, name );
}
/******************************************************************************
* SHELL_SaveRegistry [Internal]
*/
void SHELL_SaveRegistry( void )
{
const char *confdir = get_config_dir();
struct set_registry_levels_request *req = get_req_buffer();
char *fn;
int all = PROFILE_GetWineIniBool( "registry", "SaveOnlyUpdatedKeys", 1 );
int version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 1 ) ? 2 : 1;
/* set saving level (0 for saving everything, 1 for saving only modified keys) */
req->current = 1;
req->saving = !all;
req->version = version;
server_call( REQ_SET_REGISTRY_LEVELS );
if (!(fn = HeapAlloc( GetProcessHeap(), 0, MAX_PATHNAME_LEN )))
{
ERR( "Not enough memory to save registry\n" );
return;
}
if (PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1))
{
if (PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "", fn, MAX_PATHNAME_LEN ))
save_key( HKEY_CURRENT_USER, fn );
if (PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "", fn, MAX_PATHNAME_LEN ))
save_key( HKEY_LOCAL_MACHINE, fn );
if (PROFILE_GetWineIniString( "Registry", "AltUserFile", "", fn, MAX_PATHNAME_LEN ))
save_key( HKEY_USERS, fn );
}
if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1))
{
char *str;
strcpy( fn, confdir );
str = fn + strlen(fn);
*str++ = '/';
strcpy( str, SAVE_CURRENT_USER );
save_key( HKEY_CURRENT_USER, fn );
strcpy( str, SAVE_LOCAL_MACHINE );
save_key( HKEY_LOCAL_MACHINE, fn );
strcpy( str, SAVE_LOCAL_USERS_DEFAULT );
save_key( HKEY_USERS, fn );
}
HeapFree( GetProcessHeap(), 0, fn );
}
/* Periodic save callback */
static void CALLBACK periodic_save( ULONG_PTR dummy )
{
SHELL_SaveRegistry();
}
/************************ LOAD Registry Function ****************************/
@ -554,7 +405,7 @@ static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
free(buf);
return 0;
}
if (ver!=REGISTRY_SAVE_VERSION) {
if (ver!=1) {
if (ver == 2) /* new version */
{
HANDLE file;
@ -1482,6 +1333,53 @@ void _w31_loadreg(void) {
return;
}
/* configure save files and start the periodic saving timer */
static void SHELL_InitRegistrySaving(void)
{
struct set_registry_levels_request *req = get_req_buffer();
int all = PROFILE_GetWineIniBool( "registry", "SaveOnlyUpdatedKeys", 1 );
int version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 1 ) ? 2 : 1;
int period = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 );
/* set saving level (0 for saving everything, 1 for saving only modified keys) */
req->current = 1;
req->saving = !all;
req->version = version;
req->period = period * 1000;
server_call( REQ_SET_REGISTRY_LEVELS );
if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1))
{
struct save_registry_atexit_request *req = get_req_buffer();
const char *confdir = get_config_dir();
char *str = req->file + strlen(confdir);
if (str + 20 > req->file + server_remaining(req->file))
{
ERR("config dir '%s' too long\n", confdir );
return;
}
strcpy( req->file, confdir );
strcpy( str, "/" SAVE_CURRENT_USER );
req->hkey = HKEY_CURRENT_USER;
server_call( REQ_SAVE_REGISTRY_ATEXIT );
strcpy( req->file, confdir );
strcpy( str, "/" SAVE_LOCAL_MACHINE );
req->hkey = HKEY_LOCAL_MACHINE;
server_call( REQ_SAVE_REGISTRY_ATEXIT );
strcpy( req->file, confdir );
strcpy( str, "/" SAVE_LOCAL_USERS_DEFAULT );
req->hkey = HKEY_USERS;
server_call( REQ_SAVE_REGISTRY_ATEXIT );
}
}
/**********************************************************************************
* SetLoadLevel [Internal]
*
@ -1495,6 +1393,7 @@ static void SetLoadLevel(int level)
req->current = level;
req->saving = 0;
req->version = 1;
req->period = 0;
server_call( REQ_SET_REGISTRY_LEVELS );
}
@ -1694,45 +1593,9 @@ void SHELL_LoadRegistry( void )
}
}
/*
* Load HKCU, get the registry location from the config
* file, if exist, load and keep going.
*/
if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
{
if (PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "", path, sizeof(path) ))
_wine_loadreg( HKEY_CURRENT_USER, path );
/*
* Load HKU, get the registry location from the config
* file, if exist, load and keep going.
*/
if (PROFILE_GetWineIniString ( "registry", "AltUserFile", "", path, sizeof(path) ))
_wine_loadreg( HKEY_USERS, path );
/*
* Load HKLM, get the registry location from the config
* file, if exist, load and keep going.
*/
if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "", path, sizeof(path) ))
_wine_loadreg( HKEY_LOCAL_MACHINE, path );
}
SHELL_InitRegistrySaving();
}
/* start the periodic saving timer */
void SHELL_InitRegistrySaving(void)
{
int save_timeout;
if (!CLIENT_IsBootThread()) return;
if ((save_timeout = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 )))
{
SERVICE_AddTimer( save_timeout * 1000000, periodic_save, 0 );
}
}
/********************* API FUNCTIONS ***************************************/

View File

@ -77,10 +77,10 @@ int main( int argc, char *argv[] )
if (debug_level) fprintf( stderr, "Server: starting (pid=%ld)\n", (long) getpid() );
select_loop();
close_registry();
if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() );
#ifdef DEBUG_OBJECTS
close_registry();
close_atom_table();
dump_objects(); /* dump any remaining objects */
#endif

View File

@ -11,9 +11,13 @@
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include "object.h"
@ -89,6 +93,21 @@ static int saving_level;
static int saving_version = 1; /* file format version */
static struct timeval next_save_time; /* absolute time of next periodic save */
static int save_period; /* delay between periodic saves (ms) */
static struct timeout_user *save_timeout_user; /* saving timer */
/* information about where to save a registry branch */
struct save_branch_info
{
struct key *key;
char *path;
};
#define MAX_SAVE_BRANCH_INFO 8
static int save_branch_count;
static struct save_branch_info save_branch_info[MAX_SAVE_BRANCH_INFO];
/* information about a file being loaded */
struct file_load_info
@ -886,16 +905,6 @@ static struct key *create_root_key( int hkey )
return key;
}
/* close the top-level keys; used on server exit */
void close_registry(void)
{
int i;
for (i = 0; i < NB_ROOT_KEYS; i++)
{
if (root_keys[i]) release_object( root_keys[i] );
}
}
/* get the registry key corresponding to an hkey handle */
static struct key *get_hkey_obj( int hkey, unsigned int access )
{
@ -1361,6 +1370,136 @@ static void save_registry( struct key *key, int handle )
}
}
/* register a key branch for being saved on exit */
static void register_branch_for_saving( struct key *key, const char *path, size_t len )
{
if (save_branch_count >= MAX_SAVE_BRANCH_INFO)
{
set_error( STATUS_NO_MORE_ENTRIES );
return;
}
if (!(save_branch_info[save_branch_count].path = memdup( path, len+1 ))) return;
save_branch_info[save_branch_count].path[len] = 0;
save_branch_info[save_branch_count].key = (struct key *)grab_object( key );
save_branch_count++;
}
/* save a registry branch to a file */
static int save_branch( struct key *key, const char *path )
{
char *p, *real, *tmp = NULL;
int fd, count = 0, ret = 0;
FILE *f;
/* get the real path */
if (!(real = malloc( PATH_MAX ))) return 0;
if (!realpath( path, real ))
{
free( real );
real = NULL;
}
else path = real;
/* test the file type */
if ((fd = open( path, O_WRONLY )) != -1)
{
struct stat st;
/* if file is not a regular file or has multiple links,
write directly into it; otherwise use a temp file */
if (!fstat( fd, &st ) && (!S_ISREG(st.st_mode) || st.st_nlink > 1))
{
ftruncate( fd, 0 );
goto save;
}
close( fd );
}
/* create a temp file in the same directory */
if (!(tmp = malloc( strlen(path) + 20 ))) goto done;
strcpy( tmp, path );
if ((p = strrchr( tmp, '/' ))) p++;
else p = tmp;
for (;;)
{
sprintf( p, "reg%x%04x.tmp", getpid(), count++ );
if ((fd = open( tmp, O_CREAT | O_EXCL | O_WRONLY, 0666 )) != -1) break;
if (errno != EEXIST) goto done;
close( fd );
}
/* now save to it */
save:
if (!(f = fdopen( fd, "w" )))
{
if (tmp) unlink( tmp );
close( fd );
goto done;
}
if (debug_level > 1)
{
fprintf( stderr, "%s: ", path );
dump_operation( key, NULL, "saving" );
}
fprintf( f, "WINE REGISTRY Version %d\n", saving_version );
if (saving_version == 2) save_subkeys( key, key, f );
else
{
update_level( key );
save_subkeys_v1( key, 0, f );
}
ret = !fclose(f);
if (tmp)
{
/* if successfully written, rename to final name */
if (ret) ret = !rename( tmp, path );
if (!ret) unlink( tmp );
free( tmp );
}
done:
if (real) free( real );
return ret;
}
/* periodic saving of the registry */
static void periodic_save( void *arg )
{
int i;
for (i = 0; i < save_branch_count; i++)
save_branch( save_branch_info[i].key, save_branch_info[i].path );
add_timeout( &next_save_time, save_period );
save_timeout_user = add_timeout_user( &next_save_time, periodic_save, 0 );
}
/* save the registry and close the top-level keys; used on server exit */
void close_registry(void)
{
int i;
for (i = 0; i < save_branch_count; i++)
{
if (!save_branch( save_branch_info[i].key, save_branch_info[i].path ))
{
fprintf( stderr, "wineserver: could not save registry branch to %s",
save_branch_info[i].path );
perror( " " );
}
release_object( save_branch_info[i].key );
}
for (i = 0; i < NB_ROOT_KEYS; i++)
{
if (root_keys[i]) release_object( root_keys[i] );
}
}
/* create a registry key */
DECL_HANDLER(create_key)
{
@ -1544,5 +1683,31 @@ DECL_HANDLER(set_registry_levels)
current_level = req->current;
saving_level = req->saving;
saving_version = req->version;
/* set periodic save timer */
if (save_timeout_user)
{
remove_timeout_user( save_timeout_user );
save_timeout_user = NULL;
}
if ((save_period = req->period))
{
if (save_period < 10000) save_period = 10000; /* limit rate */
gettimeofday( &next_save_time, 0 );
add_timeout( &next_save_time, save_period );
save_timeout_user = add_timeout_user( &next_save_time, periodic_save, 0 );
}
}
/* save a registry branch at server exit */
DECL_HANDLER(save_registry_atexit)
{
struct key *key;
if ((key = get_hkey_obj( req->hkey, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS )))
{
register_branch_for_saving( key, req->file, get_req_strlen( req, req->file ) );
release_object( key );
}
}

View File

@ -157,6 +157,7 @@ DECL_HANDLER(enum_key_value);
DECL_HANDLER(delete_key_value);
DECL_HANDLER(load_registry);
DECL_HANDLER(save_registry);
DECL_HANDLER(save_registry_atexit);
DECL_HANDLER(set_registry_levels);
DECL_HANDLER(create_timer);
DECL_HANDLER(open_timer);
@ -262,6 +263,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_delete_key_value,
(req_handler)req_load_registry,
(req_handler)req_save_registry,
(req_handler)req_save_registry_atexit,
(req_handler)req_set_registry_levels,
(req_handler)req_create_timer,
(req_handler)req_open_timer,

View File

@ -1167,11 +1167,19 @@ static void dump_save_registry_request( const struct save_registry_request *req
fprintf( stderr, " file=%d", req->file );
}
static void dump_save_registry_atexit_request( const struct save_registry_atexit_request *req )
{
fprintf( stderr, " hkey=%d,", req->hkey );
fprintf( stderr, " file=" );
dump_string( req, req->file );
}
static void dump_set_registry_levels_request( const struct set_registry_levels_request *req )
{
fprintf( stderr, " current=%d,", req->current );
fprintf( stderr, " saving=%d,", req->saving );
fprintf( stderr, " version=%d", req->version );
fprintf( stderr, " version=%d,", req->version );
fprintf( stderr, " period=%d", req->period );
}
static void dump_create_timer_request( const struct create_timer_request *req )
@ -1375,6 +1383,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_delete_key_value_request,
(dump_func)dump_load_registry_request,
(dump_func)dump_save_registry_request,
(dump_func)dump_save_registry_atexit_request,
(dump_func)dump_set_registry_levels_request,
(dump_func)dump_create_timer_request,
(dump_func)dump_open_timer_request,
@ -1478,6 +1487,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)0,
(dump_func)0,
(dump_func)0,
(dump_func)0,
(dump_func)dump_create_timer_reply,
(dump_func)dump_open_timer_reply,
(dump_func)0,
@ -1579,6 +1589,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"delete_key_value",
"load_registry",
"save_registry",
"save_registry_atexit",
"set_registry_levels",
"create_timer",
"open_timer",

View File

@ -134,27 +134,16 @@ Exclude=WM_SIZE;WM_TIMER;
; Paths must be given in /dir/dir/file.reg format.
; Wine will not understand dos file names here...
; alternate registry file name: HKCU
AltCurrentUserFile=
; alternate registry file name: HKU
AltUserFile=
; alternate registry file name: HKLM
AltLocalMachineFile=
;These are all booleans. Y/y/T/t/1 are true, N/n/F/f/0 are false.
;Defaults are read all, write to Home and Alt
;Note: it is pointless to specify alt files and neither load nor write to them.
;Defaults are read all, write to Home
; Global registries (stored in /etc)
LoadGlobalRegistryFiles=Y
; Home registries (stored in ~user/.wine/)
LoadHomeRegistryFiles=Y
; Load above registries.
LoadAltRegistryFiles=Y
; Load Windows registries from the Windows directory
LoadWindowsRegistryFiles=Y
; TRY to write all changes to home registries
WritetoHomeRegistryFiles=Y
; TRY to write all changes to alt registries
WritetoAltRegistryFiles=Y
; Use new file format
UseNewFormat=Y
; Registry periodic save timeout in seconds