6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 2011, MinGW Project
10 * Implementation of package management task scheduler and executive.
13 * This is free software. Permission is granted to copy, modify and
14 * redistribute this software, under the provisions of the GNU General
15 * Public License, Version 3, (or, at your option, any later version),
16 * as published by the Free Software Foundation; see the file COPYING
17 * for licensing details.
19 * Note, in particular, that this software is provided "as is", in the
20 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
21 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
22 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
23 * MinGW Project, accept liability for any damages, however caused,
24 * arising from the use of this software.
36 /* FIXME: temporarily establish "install" behaviour as if the
37 * "--reinstall" option is selected; remove this kludge, when we
38 * have an effective "uninstall" implementation, and have provided
39 * a mechanism for specifying options.
41 #define pkgOptionSelected( OPT ) OPT
42 #define PKG_OPTION_REINSTALL true
44 EXTERN_C const char *action_name( unsigned long index )
46 /* Define the keywords used on the mingw-get command line,
47 * to specify the package management actions to be performed,
48 * mapping each to a unique action code index.
50 static const char* action_id[] =
52 "no change", /* unused; zero cannot test true in a bitwise test */
53 "remove", /* remove a previously installed package */
54 "install", /* install a new package */
55 "upgrade", /* upgrade previously installed packages */
57 "list", /* list packages and display related information */
58 "show", /* a synonym for "list" */
60 "update" /* update local copy of repository catalogues */
63 /* For specified "index", return a pointer to the associated keyword,
64 * or NULL, if "index" is outside the defined action code range.
66 return ((index >= 0) && (index < end_of_actions))
71 EXTERN_C int action_code( const char* request )
73 /* Match an action keyword specified on the command line
74 * to an entry from the above list...
76 int lencode = strlen( request );
79 for( index = 0; index < end_of_actions; index++ )
81 /* Try all defined keywords in turn, until we find a match
82 * or we run out of definitions...
84 if( strncmp( request, action_name( index ), lencode ) == 0 )
86 * for a successful match...
87 * immediately return the associated action code index.
92 /* If we get to here, the specified keyword was not matched;
93 * signal this, by returning -1.
98 /* To circumvent MS-Windows restrictions on deletion and/or overwriting
99 * executable and shared object files, while their respective code is in
100 * use by a running application, and to facilitate upgrade of mingw-get
101 * itself, while it is the running application performing the upgrade,
102 * we introduce a "rites of passage" work around. The first phase of
103 * this is invoked immediately on process start up, but the second
104 * phase is deferred...
106 #define IMPLEMENT_INITIATION_RITES PHASE_TWO_RITES
109 * ...until we know for sure that a self-upgrade has been scheduled...
111 RITES_INLINE bool self_upgrade_rites( const char *name )
113 /* ...as determined by inspection of package names, and deferring
114 * the rite as "pending" until a request to process "mingw-get-bin"
115 * is actually received...
117 pkgSpecs pkg( name );
118 bool pending = ((name = pkg.GetComponentClass()) == NULL)
119 || (strcmp( name, "bin" ) != 0) || ((name = pkg.GetPackageName()) == NULL)
120 || (strcmp( name, "mingw-get" ) != 0);
124 * We've just identified a request to process "mingw-get-bin";
125 * thus the requirement to invoke the "self upgrade rites" has
126 * now become immediate, so do it...
130 /* Finally, return the requirement state as it now is, whether it
131 * remains "pending" or not, so that the caller may avoid checking
132 * the requirement for invoking the "self upgrade rites" process,
133 * after it has already been requested.
138 pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before )
140 /* Construct an appropriately initialised non-specific pkgActionItem...
142 flags = 0; /* no specific action yet assigned */
144 min_wanted = NULL; /* no minimum package version constraint... */
145 max_wanted = NULL; /* nor any maximum version */
147 /* Initialise package selection to NONE, for this action... */
148 selection[to_remove] = selection[to_install] = NULL;
150 /* Insert this item at a specified location in the actions list.
157 pkgActionItem::Append( pkgActionItem *item )
159 /* Add an "item" to an ActionItems list, attaching it immediately
160 * after the item referenced by the "this" pointer; nominally "this"
161 * refers to the last entry in the list, resulting in a new item
162 * being appended to the list, but the implementation preserves
163 * integrity of any following list items, thus also fulfilling
164 * an "insert after this" function.
168 * No list exists yet;
169 * return "item" as first and only entry in new list.
173 /* Ensure "item" physically exists, or if not, create a generic
174 * placeholder in which to construct it...
176 if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
178 * ...bailing out if no such placeholder can be created.
182 /* Maintain list integrity...
184 if( (item->next = next) != NULL )
186 * ...moving any existing items which already follow the insertion
187 * point in the list structure, to follow the newly added "item".
191 /* Set the new item's own reference pointer, to establish its list
192 * attachment point...
196 /* ...and attach it immediately after that point.
202 pkgActionItem::Insert( pkgActionItem *item )
204 /* Add an "item" to an ActionItems list, inserting it immediately
205 * before the item referenced by the "this" pointer.
209 * No list exists yet;
210 * return "item" as first and only entry in new list.
214 /* Ensure "item" physically exists, or if not, create a generic
215 * placeholder in which to construct it...
217 if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
219 * ...bailing out if no such placeholder can be created.
223 /* Maintain list integrity...
225 if( (item->prev = prev) != NULL )
227 * ...moving any existing items which already precede the insertion
228 * point in the list structure, to precede the newly added "item".
232 /* Set the new item's own reference pointer, to establish the item
233 * currently at the attachment point, as its immediate successor...
237 /* ...and attach it, immediately preceding that point.
243 pkgActionItem::Schedule( unsigned long action, pkgActionItem& item )
245 /* Make a copy of an action item template (which may exist in
246 * a volatile scope) on the heap, assign the requested action,
247 * and return it for inclusion in the task schedule.
249 pkgActionItem *rtn = new pkgActionItem(); *rtn = item;
250 rtn->flags = action | (rtn->flags & ~ACTION_MASK);
255 pkgActionItem::GetReference( pkgActionItem& item )
257 /* Check for a prior reference, within the task schedule,
258 * for the package specified for processing by "item".
261 if( (pkg = item.Selection()->GetParent()) != NULL )
263 /* We have a pointer to the XML database entry which identifies
264 * the package containing the release specified as the selection
265 * associated with "item"; walk the chain of prior entries in
268 for( pkgActionItem* item = this; item != NULL; item = item->prev )
270 /* ...and if we find another item holding an identical pointer,
271 * (i.e. to the same package), we return it...
273 if( item->Selection()->GetParent() == pkg )
278 /* If we get to here, there is no prior action scheduled for the
279 * specified package, so we return a NULL pointer...
284 pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
286 /* Assign "package" as the "selection" for the referring action item,
287 * provided it matches the specified selection criteria and it represents
288 * a more recent release than any current selection.
290 pkgSpecs test( package );
292 /* Establish the selection criteria...
294 pkgSpecs min_fit( min_wanted );
295 pkgSpecs max_fit( max_wanted );
297 /* Choose one of the above, as a basis for identification of
298 * a correct package-component match...
300 pkgSpecs& fit = min_wanted ? min_fit : max_fit;
302 /* Verify that "package" fulfills the selection criteria...
304 if( match_if_explicit( test.GetComponentClass(), fit.GetComponentClass() )
305 && match_if_explicit( test.GetComponentVersion(), fit.GetComponentVersion() )
306 && ((max_wanted == NULL) || ((flags & STRICTLY_LT) ? (test < max_fit) : (test <= max_fit)))
307 && ((min_wanted == NULL) || ((flags & STRICTLY_GT) ? (test > min_fit) : (test >= min_fit))) )
309 /* We have the correct package component, and it fits within
310 * the allowed range of release versions...
312 pkgSpecs last( Selection() );
315 * It is also more recent than the current selection,
316 * so we now replace that...
318 selection[to_install] = package;
321 /* Whatever choice we make, we return the resultant selection...
326 pkgActionItem* pkgXmlDocument::Schedule
327 ( unsigned long action, pkgActionItem& item, pkgActionItem* rank )
329 /* Schedule an action item with a specified ranking order in
330 * the action list, (or at the end of the list if no ranking
331 * position is specified)...
333 pkgActionItem *ref = rank ? rank : actions;
335 /* Don't reschedule, if we already have a prior matching item...
337 if( (actions->GetReference( item ) == NULL)
339 * ...but, when we don't, and when this request produces a valid
340 * package reference, we raise a new scheduling request...
342 && ((ref = ref->Schedule( action, item )) != NULL)
343 && ((ref->Selection() != NULL) || (ref->Selection( to_remove ) != NULL)) )
345 /* ...and, when successfully raised, add it to the task list...
349 * ...at the specified ranking position, if any...
351 return rank->Insert( ref );
354 /* ...otherwise, at the end of the list.
356 return actions = actions->Append( ref );
359 /* If we get to here, then no new action was scheduled; we simply
360 * return the current insertion point in the task list.
365 void pkgActionItem::Execute()
369 pkgActionItem *current = this;
370 bool init_rites_pending = true;
371 while( current->prev != NULL ) current = current->prev;
372 DownloadArchiveFiles( current );
373 while( current != NULL )
375 /* Print a notification of the installation process to be
376 * performed, identifying the package to be processed.
379 if( (tarname = current->Selection()->GetPropVal( tarname_key, NULL )) == NULL )
380 tarname = current->Selection( to_remove )->GetPropVal( tarname_key, value_unknown );
381 dmh_printf( "%s: %s\n", action_name(current->flags & ACTION_MASK), tarname );
383 /* Check for any outstanding requirement to invoke the
384 * "self upgrade rites" process, so that we may install an
385 * upgrade for mingw-get itself...
387 if( init_rites_pending )
389 * ...discontinuing the check once this has been completed,
390 * since it need not be performed more than once.
392 init_rites_pending = self_upgrade_rites( tarname );
394 if( (current->flags & ACTION_REMOVE) == ACTION_REMOVE )
396 /* The selected package has been marked for removal, either explicitly,
397 * or as an implicit prerequisite for upgrade; search the installed system
398 * manifest, to identify the specific version (if any) to be removed.
400 * FIXME: This implementation is a stub, to be rewritten when the system
401 * manifest structure has been specified and implemented.
403 if( current->Selection( to_remove ) != NULL )
404 dmh_printf( " FIXME:pkgRemove<stub>:not removing %s\n",
405 current->Selection( to_remove )->GetPropVal( tarname_key, value_unknown )
409 if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
411 /* The selected package has been marked for installation, either explicitly,
412 * or implicitly to complete a package upgrade.
414 pkgXmlNode *tmp = current->Selection( to_remove );
415 if( pkgOptionSelected( PKG_OPTION_REINSTALL ) )
416 current->selection[ to_remove ] = NULL;
417 pkgInstall( current );
418 current->selection[ to_remove ] = tmp;
421 /* Proceed to next package with scheduled actions.
423 current = current->next;
428 pkgActionItem::~pkgActionItem()
431 * The package version range selectors, "min_wanted" and "max_wanted",
432 * are always allocated storage space on the heap; we need to free that,
433 * before we destroy the reference pointers.
435 if( (max_wanted != NULL) && (max_wanted != min_wanted) )
437 * "max_wanted" is non-NULL, and is distinct, (i.e. it doesn't
438 * represent an equality constraint which shares a reference with
439 * "min_wanted"); we need to free it independently.
441 free( (void *)(max_wanted) );
443 if( min_wanted != NULL )
445 * "min_wanted" is non-NULL; we don't care if it is distinct,
446 * because if not, freeing it is required anyway, to also free
447 * the same memory referenced by "max_wanted".
449 free( (void *)(min_wanted) );
452 /* $RCSfile$: end of file */