From c970904c2c84a527923bc49757ddc7416665848e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 16 Apr 2000 17:21:13 +0000 Subject: [PATCH] Save the registry on server exit without client intervention. Removed "alt" registry files since we now have symlinks and WINEPREFIX to replace them. --- documentation/wine.conf.man.in | 24 +--- include/server.h | 12 +- include/shell.h | 2 - loader/main.c | 4 - misc/registry.c | 237 +++++++-------------------------- server/main.c | 2 +- server/registry.c | 185 +++++++++++++++++++++++-- server/request.h | 2 + server/trace.c | 13 +- wine.ini | 13 +- 10 files changed, 253 insertions(+), 241 deletions(-) diff --git a/documentation/wine.conf.man.in b/documentation/wine.conf.man.in index 05877885aff..456e761c813 100644 --- a/documentation/wine.conf.man.in +++ b/documentation/wine.conf.man.in @@ -270,26 +270,6 @@ Use Win95-like window displays or Win3.1-like window displays. .PP .B [Registry] .br -.I format: AltCurrentUserFile= -.br -alternate registry file name: HKEY_CURRENT_USER -.PP -.I format: AltUserFile= -.br -alternate registry file name: HKKEY_USERS -.PP -.I format: AltLocalMachineFile= -.br -alternate registry file name: HKEY_LOCAL_MASCHINE -.PP -.I format: LoadAltRegistryFiles= -.br -Load above registries. -.PP -.I format: WritetoAltRegistryFiles= -.br -TRY to write all changes to alt registries -.PP .I format: LoadGlobalRegistryFiles= .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 diff --git a/include/server.h b/include/server.h index 3cc76154e39..3ccf97e5204 100644 --- a/include/server.h +++ b/include/server.h @@ -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 */ diff --git a/include/shell.h b/include/shell.h index 31e909d218a..8a5e5c6c57b 100644 --- a/include/shell.h +++ b/include/shell.h @@ -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); diff --git a/loader/main.c b/loader/main.c index 4ad29566625..af36a08216f 100644 --- a/loader/main.c +++ b/loader/main.c @@ -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 ); } diff --git a/misc/registry.c b/misc/registry.c index 6ec0a0d1e92..8dc7c2757a7 100644 --- a/misc/registry.c +++ b/misc/registry.c @@ -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 ); } @@ -1693,46 +1592,10 @@ void SHELL_LoadRegistry( void ) if (fn != path) HeapFree( GetProcessHeap(), 0, fn ); } } - - /* - * 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 ***************************************/ diff --git a/server/main.c b/server/main.c index 79f0f2b5083..6686c11c5a6 100644 --- a/server/main.c +++ b/server/main.c @@ -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 diff --git a/server/registry.c b/server/registry.c index f31237a9873..308046838cb 100644 --- a/server/registry.c +++ b/server/registry.c @@ -11,9 +11,13 @@ */ #include +#include +#include #include #include +#include #include +#include #include #include #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 ); + } +} diff --git a/server/request.h b/server/request.h index 23592158ddd..03943d99dc3 100644 --- a/server/request.h +++ b/server/request.h @@ -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, diff --git a/server/trace.c b/server/trace.c index a76b515f5c7..aeba4095562 100644 --- a/server/trace.c +++ b/server/trace.c @@ -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", diff --git a/wine.ini b/wine.ini index cd629f473d9..d6ce44545b8 100644 --- a/wine.ini +++ b/wine.ini @@ -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