6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2013, MinGW.org Project
10 * Implementation of the processing redirector hook, to be provided
11 * in the form of a free-standing bridge DLL, through which the setup
12 * tool requests services from the main mingw-get DLL.
15 * This is free software. Permission is granted to copy, modify and
16 * redistribute this software, under the provisions of the GNU General
17 * Public License, Version 3, (or, at your option, any later version),
18 * as published by the Free Software Foundation; see the file COPYING
19 * for licensing details.
21 * Note, in particular, that this software is provided "as is", in the
22 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
25 * MinGW Project, accept liability for any damages, however caused,
26 * arising from the use of this software.
29 static const char *setup_key = "setup";
31 #define WIN32_LEAN_AND_MEAN
32 #define IMPLEMENTATION_LEVEL PACKAGE_BASE_COMPONENT
47 #include <sys/types.h>
51 static const char *internal_error = "internal error";
52 #define MSG_INTERNAL_ERROR(MSG) "%s: "MSG_##MSG"\n", internal_error
54 #define MSG_INVALID_REQUEST "invalid request code specified"
55 #define MSG_NOTIFY_MAINTAINER "please report this to the mingw-get maintainer"
57 #define PROGRESS_METER_CLASS setupProgressMeter
59 class PROGRESS_METER_CLASS: public pkgProgressMeter
61 /* Specialisation of the pkgProgressMeter class, through which
62 * the pkgXmlDocument::BindRepositories() method sends progress
63 * reports to the setup tool dialogue.
66 PROGRESS_METER_CLASS( HWND, pkgXmlDocument & );
67 ~PROGRESS_METER_CLASS(){ dbase.DetachProgressMeter( this ); }
69 virtual int Annotate( const char *, ... );
70 virtual void SetRange( int, int );
71 virtual void SetValue( int );
74 pkgXmlDocument &dbase;
75 HWND annotation, count, lim, frac, progress_bar;
76 void PutVal( HWND, const char *, ... );
80 /* This class requires a specialised constructor...
82 #define IDD( DLG, ITEM ) GetDlgItem( DLG, IDD_PROGRESS_##ITEM )
83 PROGRESS_METER_CLASS::PROGRESS_METER_CLASS( HWND owner, pkgXmlDocument &db ):
84 dbase( db ), annotation( IDD( owner, MSG ) ), progress_bar( IDD( owner, BAR ) ),
85 count( IDD( owner, VAL ) ), lim( IDD( owner, MAX ) ), frac( IDD( owner, PCT ) )
87 /* ...which binds the progress repporter directly to the
88 * invoking pkgXmlDocument class object, before setting the
89 * initial item count to zero, from a minimal anticipated
92 dbase.AttachProgressMeter( this );
93 SetRange( 0, 1 ); SetValue( 0 );
96 /* The remaining methods of the class are abstracted from the
97 * generic progress metering implementation.
99 #include "pmihook.cpp"
102 void initialise_profile( void )
104 /* Helper function to ensure that profile.xml exists in the
105 * mingw-get database directory, either as a pre-existing file,
106 * or by creating it as a copy of defaults.xml
109 const char *profile = xmlfile( profile_key, NULL );
110 if( (profile != NULL) && (access( profile, F_OK ) != 0)
111 && ((copyout = set_output_stream( profile, 0644 )) >= 0) )
113 /* No profile.xml file currently exists, but we are able
116 const char *default_profile;
117 if( ((default_profile = xmlfile( defaults_key, NULL )) != NULL)
118 && ((copyin = open( default_profile, _O_RDONLY | _O_BINARY )) >= 0) )
120 /* ...whereas the defaults.xml DOES exist, and we are able
121 * to read it, so set up a transfer buffer, through which we
122 * may copy its content to profile.xml
124 char buf[BUFSIZ]; ssize_t count;
125 while( (count = read( copyin, buf, BUFSIZ )) > 0 )
126 write( copyout, buf, count );
128 /* When the entire content of defaults.xml has been copied,
129 * close both file streams, saving both files.
135 { /* We were unable to open defaults.xml for copying, but we
136 * already hold an open stream handle for profile.xml; close
137 * it, then unlink, to discard the resulting empty file.
142 /* Attempting to open defaults.xml, whether successful or not,
143 * has left us with a heap memory allocation for its path name.
144 * We don't need it any more; free it so we don't leak memory.
146 free( (void *)(default_profile) );
148 /* Similarly, we have a heap memory allocation for the path name
149 * of profile.xml; we must also avoid leaking it.
151 free( (void *)(profile) );
155 void update_database( pkgSetupAction *setup )
157 /* Helper function to initiate an XML database update, based on
158 * a restricted (custom) profile, to ensure that the installation
159 * of mingw-get itself is properly recorded.
162 pkgXmlDocument dbase( dfile = xmlfile( setup_key ) );
163 if( dbase.IsOk() && (dbase.BindRepositories( false ) != NULL) )
165 /* Having successfully loaded the restricted profile, load the
166 * map of the installation it specifies, and update it to reflect
167 * the installation of the mingw-get packages we are currently
170 dbase.LoadSystemMap();
171 setup->UpdateDatabase( dbase );
173 /* We're finished with the setup.xml profile; delete it, and free
174 * the memory which xmlfile() allocated to store its path name.
177 free( (void *)(dfile) );
181 void update_catalogue( HWND owner )
183 /* Helper function to ensure that all locally installed catalogue
184 * files are synchronised with their latest versions, as hosted on
185 * their respective repository servers.
188 pkgXmlDocument dbase( dfile = xmlfile( profile_key ) );
191 /* The XML package database has been successfully loaded;
192 * ensure that the local catalogue is synchronised with the
193 * with the up-to-date repository representation.
195 setupProgressMeter watch( owner, dbase );
196 dbase.BindRepositories( true );
198 "Catalogue update completed; please check 'Details' pane for errors."
201 /* We're finished with the path name for the profile.xml file;
202 * release the memory which xmlfile() allocated to store it.
204 free( (void *)(dfile) );
207 EXTERN_C __declspec(dllexport)
208 void setup_hook( unsigned int request, va_list argv )
210 /* Single entry point API function, through which the setup tool
211 * directs all requests to its own delay-loaded DLL, and hence to
212 * mingw-get-0.dll itself.
215 { case SETUP_HOOK_DMH_BIND:
216 /* Initialisation hook, through which the setup tool makes its
217 * already initialised diagnostic message handler available to
218 * to DLL functions which it invokes.
220 dmh_bind( va_arg( argv, dmhTypeGeneric * ) );
223 case SETUP_HOOK_POST_INSTALL:
224 /* This is the principal entry point, through which the setup
225 * tool hands off the final phases of mingw-get installation to
226 * the XML database management functions within the DLLs.
228 initialise_profile();
229 update_database( va_arg( argv, pkgSetupAction * ) );
230 update_catalogue( va_arg( argv, HWND ) );
234 /* We should never get to here; it's a programming error in
235 * the setup tool, if we do.
237 dmh_notify( DMH_ERROR, MSG_INTERNAL_ERROR( INVALID_REQUEST ) );
238 dmh_notify( DMH_ERROR, MSG_INTERNAL_ERROR( NOTIFY_MAINTAINER ) );
242 class pkgXmlNodeStack
244 /* A convenience class for managing a list of pkgXmlNodes
248 inline pkgXmlNodeStack *push( pkgXmlNode * );
249 inline pkgXmlNodeStack *pop( pkgXmlNode ** );
252 pkgXmlNodeStack *next;
256 inline pkgXmlNodeStack *pkgXmlNodeStack::push( pkgXmlNode *node )
258 /* Method to push a pkgXmlNode into a new frame, on the top of
259 * the stack, returning the new stack pointer.
261 pkgXmlNodeStack *rtn;
262 if( (rtn = new pkgXmlNodeStack) != NULL )
264 /* We successfully allocated storage space for the new stack
265 * entry; populate it with the specified pkgXmlNode date, and
266 * link it to the existing stack chain...
268 rtn->entry = node; rtn->next = this;
270 /* ...before returning the new top-of-stack pointer.
274 /* If we get to here, we were unable to expand the stack to
275 * accommodate any new entry; don't move the stack pointer.
280 inline pkgXmlNodeStack *pkgXmlNodeStack::pop( pkgXmlNode **node )
282 /* Method to pop a pkgXmlNode from the top of the stack, while
283 * saving a reference pointer for it, and returning the updated
288 * The stack is empty; there's nothing more to do!
292 /* When the stack is NOT empty, capture the reference pointer
293 * for the SECOND entry (if any), store the top entry, delete
294 * its stack frame, and return the new stack pointer.
296 pkgXmlNodeStack *rtn = next; *node = entry;
297 delete this; return rtn;
300 void pkgSetupAction::UpdateDatabase( pkgXmlDocument &dbase )
302 /* Method to ensure that the mingw-get package components which are
303 * specified in the setup actions list are recorded as "installed", in
304 * the installation database manifests.
308 /* The setup actions list is not empty; ensure that we commence
309 * processing from its first entry...
311 pkgSetupAction *current = this;
312 while( current->prev != NULL ) current = current->prev;
313 dmh_notify( DMH_INFO, "%s: updating installation database\n", setup_key );
314 while( current != NULL )
316 /* ...then processing all entries sequentially, in turn,
317 * parse the package tarname specified in the current action
318 * entry, to identify the associated package name, component
319 * class and subsystem name.
321 const char *name_fmt = "%s-%s";
322 pkgSpecs lookup( current->package_name );
323 char lookup_name[1 + strlen( current->package_name )];
324 const char *component = lookup.GetComponentClass();
325 const char *subsystem = lookup.GetSubSystemName();
326 if( (component != NULL) && *component )
328 * The package name is qualified by an explicit component
329 * name; form the composite package name string.
331 sprintf( lookup_name, name_fmt, lookup.GetPackageName(), component );
333 /* There is no explicit component name; just save a copy
334 * of the unqualified package name.
336 strcpy( lookup_name, lookup.GetPackageName() );
338 /* Locate the corresponding component package entry, if any,
339 * in the package catalogue.
341 if( dbase.FindPackageByName( lookup_name, subsystem ) != NULL )
343 /* Lookup was successful; now search the installation records,
344 * if any, for any matching package entry.
346 pkgXmlNodeStack *stack = NULL;
347 pkgXmlNode *sysroot = dbase.GetRoot()->GetSysRoot( subsystem );
348 pkgXmlNode *installed = sysroot->FindFirstAssociate( installed_key );
349 while( installed != NULL )
351 /* There is at least one installation record; walk the chain
352 * of all such records...
354 const char *tarname = installed->GetPropVal( tarname_key, NULL );
355 if( tarname != NULL )
357 /* ...extracting package and component names from the tarname
358 * specification within each...
360 pkgSpecs ref( tarname );
361 char ref_name[1 + strlen( tarname )];
362 if( ((component = ref.GetComponentClass()) != NULL) && *component )
364 * ...once again forming the composite name, when applicable...
366 sprintf( ref_name, name_fmt, ref.GetPackageName(), component );
368 /* ...or simply storing the unqualified package name if not.
370 strcpy( ref_name, ref.GetPackageName() );
372 /* Check for a match between the installed package name, and
373 * the name we wish to record as newly installed...
375 if( (strcasecmp( ref_name, lookup_name ) == 0)
376 && (strcasecmp( tarname, current->package_name ) != 0) )
378 * ...pushing the current installation record on to the
379 * update stack, in case of a match...
381 stack = stack->push( installed );
383 /* ...then move on to the next installation record, if any.
385 installed = installed->FindNextAssociate( installed_key );
388 /* Create a temporary package "release" descriptor...
390 pkgXmlNode *reference_hook = new pkgXmlNode( release_key );
391 if( reference_hook != NULL )
393 /* ...which we may conveniently attach to the root
394 * of the XML catalogue tree.
396 dbase.GetRoot()->AddChild( reference_hook );
397 reference_hook->SetAttribute( tarname_key, current->package_name );
399 /* Run the installer...
401 pkgTarArchiveInstaller registration_server( reference_hook );
402 if( registration_server.IsOk() )
404 /* ...reporting the installation as a "registration" of
405 * the specified package, but...
407 dmh_notify( DMH_INFO, "%s: register %s\n",
408 setup_key, current->package_name
410 /* ...noting that the package content has already been
411 * "installed" by the setup tool, but without recording
412 * any details, we run this without physically extracting
413 * any files, to capture the side effect of compiling an
414 * installation record.
416 registration_server.SaveExtractedFiles( false );
417 registration_server.Process();
419 /* With the installation record safely compiled, we may
420 * discard the temporary "release" descriptor from which
423 dbase.GetRoot()->DeleteChild( reference_hook );
426 /* When the update stack, constructed above, is not empty...
429 { while( stack != NULL )
431 /* ...pop each installation record, which is to be updated,
432 * off the update stack, in turn...
435 pkgXmlNode *installed;
436 stack = stack->pop( &installed );
437 if( (tarname = installed->GetPropVal( tarname_key, NULL )) != NULL )
439 /* ...identify its associated installed files manifest, and
440 * disassociate it from the sysroot of the current installation;
441 * (note that this automatically deletes the manifest itself, if
442 * it is associated with no other sysroots).
444 pkgManifest inventory( package_key, tarname );
445 inventory.DetachSysRoot( sysroot->GetPropVal( id_key, subsystem ) );
447 /* Delete the installation record from the current sysroot...
449 sysroot->DeleteChild( installed );
451 /* ...and mark the sysroot record as "modified", as a result of
452 * all preceding updates.
454 sysroot->SetAttribute( modified_key, value_yes );
458 /* Repeat for all packages with an associated setup action...
460 current = current->next;
462 /* ...and finally, report completion of all database updates, while also
463 * committing all recorded changes to disk storage.
465 dmh_notify( DMH_INFO, "%s: installation database updated\n", setup_key );
466 dbase.UpdateSystemMap();
470 /* $RCSfile$: end of file */