6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2010, 2011, 2012, MinGW.org Project
10 * Implementation of the primary package installation and package
11 * manifest recording methods.
14 * This is free software. Permission is granted to copy, modify and
15 * redistribute this software, under the provisions of the GNU General
16 * Public License, Version 3, (or, at your option, any later version),
17 * as published by the Free Software Foundation; see the file COPYING
18 * for licensing details.
20 * Note, in particular, that this software is provided "as is", in the
21 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
22 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
23 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
24 * MinGW Project, accept liability for any damages, however caused,
25 * arising from the use of this software.
40 EXTERN_C const char *hashed_name( int, const char *, const char * );
42 pkgManifest::pkgManifest( const char *tag, const char *tarname )
44 /* Construct an in-memory image for processing a package manifest.
46 * We begin by initialising this pair of reference pointers,
47 * assuming that this manifest may become invalid...
52 /* Then we check that a package tarname has been provided...
56 /* ...in which case, we proceed to set up the reference data...
61 /* Generate a hashed signature for the package manifest
62 * record, and derive an associated database file path name
63 * from it. Note that this hash is returned in 'malloc'ed
64 * memory, which we must later free. Also note that there
65 * are eight possible hashes, to mitigate hash collision,
66 * each of which is denoted by retry modulo eight; we make
67 * an initial pass through the possible hashes, looking for
68 * an existing installation map for this sysroot, loading
69 * it immediately if we find it. Otherwise, we continue
70 * with a second cycle, (retry = 8..15), looking for the
71 * first generated hash with no associated file; we then
72 * use this to create a new installation record file.
74 pkgXmlDocument *chkfile;
75 const char *signame = hashed_name( retry++, manifest_key, tarname );
76 const char *sigfile = xmlfile( signame, NULL );
78 /* Check for an existing file associated with the hash value...
80 if( (chkfile = new pkgXmlDocument( sigfile ))->IsOk() )
82 /* ...such a file does exist, but we must still check
83 * that it relates to the specified package...
87 /* ...however, we only perform this check during the
88 * first pass through the possible hashes; (second time
89 * through, we are only interested in a hash which does
90 * not have an associated file; note that the first pass
91 * through is for retry = 0..7, but by the time we get
92 * to here we have already incremented 7 to become 8,
93 * hence the check for retry < 9).
95 pkgXmlNode *root, *rel;
96 const char *pkg_id, *pkg_tarname;
97 if( ((root = chkfile->GetRoot()) != NULL)
98 && ((root->IsElementOfType( tag )))
99 && ((pkg_id = root->GetPropVal( id_key, NULL )) != NULL)
100 && ((strcmp( pkg_id, signame ) == 0))
101 && ((rel = root->FindFirstAssociate( release_key )) != NULL)
102 && ((pkg_tarname = rel->GetPropVal( tarname_key, NULL )) != NULL)
103 && ((pkg_strcmp( pkg_tarname, tarname ))) )
105 /* This is the manifest file we require...
106 * assign it for return, and force an early exit
107 * from the retry loop.
115 /* Once more noting the prior increment of retry, such
116 * that it has now become 8 for the hash generation with
121 /* ...we have exhausted all possible hash references,
122 * finding no existing manifest for the specified package;
123 * immediately assign the current (unused) manifest name,
124 * and initialise its root element...
126 pkgXmlNode *root = new pkgXmlNode( tag );
127 (manifest = chkfile)->AddDeclaration( "1.0", "UTF-8", value_yes );
128 root->SetAttribute( id_key, signame );
129 manifest->SetRoot( root );
131 /* ...a container, to be filled later, for recording the
132 * data associated with the specific release of the package
133 * to which this manifest will refer...
135 pkgXmlNode *ref = new pkgXmlNode( release_key );
136 ref->SetAttribute( tarname_key, tarname );
137 root->AddChild( ref );
139 /* ...a further container, in which to record the sysroot
140 * associations for installed instances of this package...
142 ref = new pkgXmlNode( reference_key );
143 root->AddChild( ref );
145 /* ...and one in which to accumulate the content manifest.
147 inventory = new pkgXmlNode( manifest_key );
148 root->AddChild( inventory );
150 /* Finally, having constructed a skeletal manifest,
151 * force an immediate exit from the retry loop...
156 /* Before abandoning our references to the current hash
157 * signature, and the path name for the associated XML file,
158 * free the memory allocated for them.
160 free( (void *)(sigfile) );
161 free( (void *)(signame) );
163 /* If we have not yet exhausted all possible hashed file names...
167 * ...free the heap memory allocated for the current (unsuitable)
168 * association, so making its "chkfile" reference pointer available
169 * for the next trial, without incurring a memory leak.
176 void pkgManifest::BindSysRoot( pkgXmlNode *sysroot, const char *reference_tag )
178 /* Identify the package associated with the current manifest as
179 * having been installed within the specified sysroot, by tagging
180 * with a manifest reference to the sysroot identification key.
183 if( ((id = sysroot->GetPropVal( id_key, NULL )) != NULL) && (*id != '\0') )
185 /* The specified sysroot has a non-NULL, non-blank key;
186 * check for a prior reference to the same key, within the
187 * "references" section of the manifest...
189 pkgXmlNode *map, *ref;
190 if( ((ref = manifest->GetRoot()) != NULL)
191 && ((ref->IsElementOfType( reference_tag )))
192 && ((map = ref->FindFirstAssociate( reference_key )) == NULL) )
194 /* This manifest doesn't yet have a "references" section;
195 * create and add one now...
197 map = new pkgXmlNode( reference_key );
198 ref->AddChild( map );
201 /* Examine any existing sysroot references within this manifest...
203 ref = map->FindFirstAssociate( sysroot_key );
204 while( (ref != NULL) && (strcmp( id, ref->GetPropVal( id_key, id )) != 0) )
206 * ...progressing to the next reference, if any, until...
208 ref = ref->FindNextAssociate( sysroot_key );
210 /* ...we either matched the required sysroot key, or we
211 * ran out of existing references, without finding a match.
215 /* When no existing reference was found, add one.
217 ref = new pkgXmlNode( sysroot_key );
218 ref->SetAttribute( id_key, id );
219 map->AddChild( ref );
224 void pkgManifest::AddEntry( const char *key, const char *pathname )
226 /* Method invoked by package installers, to add file or directory
227 * entries to the tracked inventory of package content.
229 * Tracking is enabled only if the manifest structure is valid,
230 * (which must have been verified by the caller), AND an inventory
231 * table has been allocated...
233 if( inventory != NULL )
235 /* ...in which case we allocate a new tracking record, with
236 * "dir" or "file" reference key as appropriate, fill it out
237 * with the associated path name attribute, and insert it in
238 * the inventory table.
240 pkgXmlNode *entry = new pkgXmlNode( key );
241 entry->SetAttribute( pathname_key, pathname );
242 inventory->AddChild( entry );
246 pkgManifest::~pkgManifest()
248 /* Destructor for package manifest images; it releases
249 * the memory used while processing a manifest, after first
250 * committing the image to disk storage, or deleting such
251 * a disk image as appropriate.
256 /* First confirm that an identification signature has been
257 * assigned for this manifest...
259 if( ((manifest != NULL)) && ((ref = manifest->GetRoot()) != NULL)
260 && ((sigfile = ref->GetPropVal( id_key, NULL )) != NULL) )
262 /* ...and map this to a file system reference path name.
264 sigfile = xmlfile( sigfile );
266 /* Check if any current installation, as identified by
267 * its sysroot key, refers to this manifest...
269 if( ((ref = ref->FindFirstAssociate( reference_key )) != NULL)
270 && ((ref = ref->FindFirstAssociate( sysroot_key )) != NULL) )
272 * ...and if so, commit this manifest to disk...
274 manifest->Save( sigfile );
277 /* ...otherwise, this manifest is defunct, so
278 * delete any current disk copy.
282 /* Release the memory used to identify the path name for
283 * the on-disk copy of this manifest...
285 free( (void *)(sigfile) );
288 /* ...and finally, expunge its in-memory image.
294 void record_dependencies( pkgXmlNode *origin, pkgXmlNode *list )
296 /* Helper function to record dependency call-outs for the package
297 * which is specified by the XML descriptor reference at "origin",
298 * (which should nominally represent a "release" specification for
299 * the package); the call-out references are collected in the XML
300 * container referenced by "list".
302 if( (origin != NULL) && (list != NULL) )
304 /* Assume "origin" and "list" represent appropriate XML objects...
306 if( ! origin->IsElementOfType( package_key ) )
308 * ...and walk back through the "origin" tree, until we locate
309 * the top level node in the "package" specification.
311 record_dependencies( origin->GetParent(), list );
313 /* As we unwind this recursive walk back search, we copy all
314 * "requires" elements, at each intervening level, from the top
315 * "package" node until we return to the original "release"...
317 pkgXmlNode *dep = origin->FindFirstAssociate( requires_key );
320 /* ...each as a simple clone within the "list" container,
321 * (and all at a single level within the "list")...
323 list->AddChild( dep->Clone() );
325 /* ...repeating for all specified "requires" elements
328 dep = dep->FindNextAssociate( requires_key );
333 EXTERN_C void pkgRegister
334 ( pkgXmlNode *sysroot, pkgXmlNode *origin, const char *tarname, const char *pkgfile )
336 /* Search the installation records for the current sysroot...
338 const char *pkg_tarname = NULL;
339 pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
341 && ((pkg_tarname = ref->GetPropVal( tarname_key, NULL )) != NULL)
342 && ! (pkg_strcmp( tarname, pkg_tarname )) )
344 * ...continuing until we either run out of installation records,
345 * or we find an already existing reference to this package.
347 ref = ref->FindNextAssociate( installed_key );
349 /* When we didn't find an appropriate existing installation record,
350 * we instantiate a new one...
352 if( (ref == NULL) && ((ref = new pkgXmlNode( installed_key )) != NULL) )
354 /* Fill out its "tarname" attribution...
356 ref->SetAttribute( tarname_key, tarname );
357 if( pkgfile != tarname )
359 /* When the real package tarball name isn't identically
360 * the same as the canonical name, then we record the real
363 pkgXmlNode *dl = new pkgXmlNode( download_key );
364 dl->SetAttribute( tarname_key, pkgfile );
368 /* Record dependency call-outs for the installed package.
370 record_dependencies( origin, ref );
372 /* Set the 'modified' flag for, and attach the installation
373 * record to, the relevant sysroot record.
375 sysroot->SetAttribute( modified_key, value_yes );
376 sysroot->AddChild( ref );
380 EXTERN_C void pkgInstall( pkgActionItem *current )
382 /* Common handler for all package installation tasks; note that we
383 * initially assert failure, and will revert this assertion in the
384 * event of subsequent successful installation...
387 current->Assert( ACTION_INSTALL_FAILED );
388 if( (pkg = current->Selection()) != NULL )
390 /* The current action item has a valid package association...
392 if( current->HasAttribute( ACTION_DOWNLOAD ) == 0 )
394 /* ...and the required package has been successfully downloaded.
396 if( current->Selection( to_remove ) == NULL )
398 /* The selected package has either not yet been installed,
399 * or any prior installation has been removed in preparation
400 * for re-installation or upgrade.
402 const char *pkgfile, *tarname;
404 /* Before proceeding with the installation, we should invoke
405 * any associated pre-install script.
407 pkg->InvokeScript( "pre-install" );
409 /* Now, we may proceed with package installation...
411 * FIXME: the notification here is somewhat redundant, but it
412 * does maintain symmetry with the "remove" operation, and will
413 * make "upgrade" notifications more logical; in any event, it
414 * should ultimately be made conditional on a "verbose" mode
417 dmh_printf( " installing %s\n",
418 pkg->GetPropVal( tarname_key, value_unknown )
420 if( match_if_explicit( pkgfile = pkg->ArchiveName(), value_none )
421 && ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL) )
423 /* In this case, the selected package has no associated archive,
424 * (i.e. it is a "virtual" package); provided we can identify an
425 * associated "sysroot"...
428 pkgSpecs lookup( tarname );
429 if( (sysroot = pkg->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
431 * ...the installation process becomes a simple matter of
432 * recording the state of this virtual package as "installed",
433 * in the sysroot manifest, and itemising its prerequisites.
435 pkgRegister( sysroot, pkg, tarname, pkgfile );
438 { /* Here we have a "real" (physical) package to install;
439 * for the time being, we assume it is packaged in our
440 * standard "tar" archive format.
442 pkgTarArchiveInstaller install( pkg );
446 /* Update the internal record of installed state; although no
447 * running CLI instance will return to any point where it needs
448 * this, we may have been called from the GUI, and it requires
449 * consistency here, if the user revisits this package within
450 * any single active session.
452 pkg->SetAttribute( installed_key, value_yes );
454 /* Whether we just installed a virtual package or a real package,
455 * we may now run its post-install script, (if any).
457 pkg->InvokeScript( "post-install" );
459 /* When we get to here, the package should have been installed
460 * successfully; revert our original assertion of failure.
462 current->Assert( 0UL, ~ACTION_INSTALL_FAILED );
465 /* There is a prior installation of the selected package, which
466 * prevents us from proceeding; diagnose and otherwise ignore...
468 dmh_notify( DMH_ERROR,
469 "package %s is already installed\n",
470 current->Selection()->GetPropVal( tarname_key, value_unknown )
474 { /* We have a valid package selection, but the required package is
475 * not present in the local cache; this indicates that the package
476 * has never been successfully downloaded.
478 int action = current->HasAttribute( ACTION_MASK );
479 dmh_notify( DMH_ERROR, "required package file is not available\n" );
480 dmh_notify( DMH_ERROR, "cannot %s%s%s\n", action_name( action ),
481 (action == ACTION_UPGRADE) ? " to " : " ",
482 pkg->GetPropVal( tarname_key, value_unknown )
484 dmh_notify( DMH_ERROR, "due to previous download failure\n" );
489 /* $RCSfile$: end of file */