Commit c970904c authored by Alexandre Julliard's avatar Alexandre Julliard

Save the registry on server exit without client intervention.

Removed "alt" registry files since we now have symlinks and WINEPREFIX to replace them.
parent fa8b7281
......@@ -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
......
......@@ -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 */
......
......@@ -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);
......
......@@ -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 );
}
......@@ -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 );
}
}
/* 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 );
}
SHELL_InitRegistrySaving();
}
/********************* API FUNCTIONS ***************************************/
......
......@@ -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
......
......@@ -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 );
}
}
......@@ -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,
......
......@@ -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",
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment