6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 2011, 2012, 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.
37 EXTERN_C const char *action_name( unsigned long index )
39 /* Define the keywords used on the mingw-get command line,
40 * to specify the package management actions to be performed,
41 * mapping each to a unique action code index.
43 static const char* action_id[] =
45 "no change", /* unused; zero cannot test true in a bitwise test */
46 "remove", /* remove a previously installed package */
47 "install", /* install a new package */
48 "upgrade", /* upgrade previously installed packages */
50 "list", /* list packages and display related information */
51 "show", /* a synonym for "list" */
53 "update", /* update local copy of repository catalogues */
54 "licence", /* retrieve licence sources from repository */
55 "source" /* retrieve package sources from repository */
58 /* For specified "index", return a pointer to the associated keyword,
59 * or NULL, if "index" is outside the defined action code range.
61 return ((index >= 0) && (index < end_of_actions))
66 EXTERN_C int action_code( const char* request )
68 /* Match an action keyword specified on the command line
69 * to an entry from the above list...
71 int lencode = strlen( request );
74 for( index = 0; index < end_of_actions; index++ )
76 /* Try all defined keywords in turn, until we find a match
77 * or we run out of definitions...
79 if( strncmp( request, action_name( index ), lencode ) == 0 )
81 * for a successful match...
82 * immediately return the associated action code index.
87 /* If we get to here, the specified keyword was not matched;
88 * signal this, by returning -1.
93 /* To circumvent MS-Windows restrictions on deletion and/or overwriting
94 * executable and shared object files, while their respective code is in
95 * use by a running application, and to facilitate upgrade of mingw-get
96 * itself, while it is the running application performing the upgrade,
97 * we introduce a "rites of passage" work around. The first phase of
98 * this is invoked immediately on process start up, but the second
99 * phase is deferred...
101 #define IMPLEMENT_INITIATION_RITES PHASE_TWO_RITES
104 * ...until we know for sure that a self-upgrade has been scheduled...
106 RITES_INLINE bool self_upgrade_rites( const char *name )
108 /* ...as determined by inspection of package names, and deferring
109 * the rite as "pending" until a request to process "mingw-get-bin"
110 * is actually received...
112 pkgSpecs pkg( name );
113 bool pending = ((name = pkg.GetComponentClass()) == NULL)
114 || (strcmp( name, "bin" ) != 0) || ((name = pkg.GetPackageName()) == NULL)
115 || (strcmp( name, "mingw-get" ) != 0);
119 * We've just identified a request to process "mingw-get-bin";
120 * thus the requirement to invoke the "self upgrade rites" has
121 * now become immediate, so do it...
125 /* Finally, return the requirement state as it now is, whether it
126 * remains "pending" or not, so that the caller may avoid checking
127 * the requirement for invoking the "self upgrade rites" process,
128 * after it has already been requested.
133 pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before )
135 /* Construct an appropriately initialised non-specific pkgActionItem...
137 flags = 0; /* no specific action yet assigned */
139 min_wanted = NULL; /* no minimum package version constraint... */
140 max_wanted = NULL; /* nor any maximum version */
142 /* Initialise package selection to NONE, for this action... */
143 selection[to_remove] = selection[to_install] = NULL;
145 /* Insert this item at a specified location in the actions list.
152 pkgActionItem::Append( pkgActionItem *item )
154 /* Add an "item" to an ActionItems list, attaching it immediately
155 * after the item referenced by the "this" pointer; nominally "this"
156 * refers to the last entry in the list, resulting in a new item
157 * being appended to the list, but the implementation preserves
158 * integrity of any following list items, thus also fulfilling
159 * an "insert after this" function.
163 * No list exists yet;
164 * return "item" as first and only entry in new list.
168 /* Ensure "item" physically exists, or if not, create a generic
169 * placeholder in which to construct it...
171 if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
173 * ...bailing out if no such placeholder can be created.
177 /* Maintain list integrity...
179 if( (item->next = next) != NULL )
181 * ...moving any existing items which already follow the insertion
182 * point in the list structure, to follow the newly added "item".
186 /* Set the new item's own reference pointer, to establish its list
187 * attachment point...
191 /* ...and attach it immediately after that point.
197 pkgActionItem::Insert( pkgActionItem *item )
199 /* Add an "item" to an ActionItems list, inserting it immediately
200 * before the item referenced by the "this" pointer.
204 * No list exists yet;
205 * return "item" as first and only entry in new list.
209 /* Ensure "item" physically exists, or if not, create a generic
210 * placeholder in which to construct it...
212 if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
214 * ...bailing out if no such placeholder can be created.
218 /* Maintain list integrity...
220 if( (item->prev = prev) != NULL )
222 * ...moving any existing items which already precede the insertion
223 * point in the list structure, to precede the newly added "item".
227 /* Set the new item's own reference pointer, to establish the item
228 * currently at the attachment point, as its immediate successor...
232 /* ...and attach it, immediately preceding that point.
238 pkgActionItem::Schedule( unsigned long action, pkgActionItem& item )
240 /* Make a copy of an action item template (which may exist in
241 * a volatile scope) on the heap, assign the requested action,
242 * and return it for inclusion in the task schedule.
244 pkgActionItem *rtn = new pkgActionItem(); *rtn = item;
245 if( pkgOptions()->Test( OPTION_REINSTALL ) == OPTION_REINSTALL )
247 * When the user specified the "--reinstall" option, either
248 * explicitly, or implied by "--download-only", (or even as a
249 * side effect of "--print-uris"), we MUST enable a download
250 * action, in case it is required to complete the request.
252 action |= ACTION_DOWNLOAD;
253 rtn->flags = action | (rtn->flags & ~ACTION_MASK);
258 pkgActionItem::GetReference( pkgActionItem& item )
260 /* Check for a prior reference, within the task schedule,
261 * for the package specified for processing by "item".
264 if( (pkg = item.Selection()->GetParent()) != NULL )
266 /* We have a pointer to the XML database entry which identifies
267 * the package containing the release specified as the selection
268 * associated with "item"; walk the chain of prior entries in
271 for( pkgActionItem* item = this; item != NULL; item = item->prev )
273 /* ...and if we find another item holding an identical pointer,
274 * (i.e. to the same package), we return it...
276 if( item->Selection()->GetParent() == pkg )
281 /* If we get to here, there is no prior action scheduled for the
282 * specified package, so we return a NULL pointer...
287 pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
289 /* Assign "package" as the "selection" for the referring action item,
290 * provided it matches the specified selection criteria and it represents
291 * a more recent release than any current selection.
293 pkgSpecs test( package );
295 /* Establish the selection criteria...
297 pkgSpecs min_fit( min_wanted );
298 pkgSpecs max_fit( max_wanted );
300 /* Choose one of the above, as a basis for identification of
301 * a correct package-component match...
303 pkgSpecs& fit = min_wanted ? min_fit : max_fit;
305 /* Verify that "package" fulfills the selection criteria...
307 if( match_if_explicit( test.GetComponentClass(), fit.GetComponentClass() )
308 && match_if_explicit( test.GetComponentVersion(), fit.GetComponentVersion() )
309 && ((max_wanted == NULL) || ((flags & STRICTLY_LT) ? (test < max_fit) : (test <= max_fit)))
310 && ((min_wanted == NULL) || ((flags & STRICTLY_GT) ? (test > min_fit) : (test >= min_fit))) )
312 /* We have the correct package component, and it fits within
313 * the allowed range of release versions...
315 pkgSpecs last( Selection() );
318 * It is also more recent than the current selection,
319 * so we now replace that...
321 selection[to_install] = package;
324 /* Whatever choice we make, we return the resultant selection...
329 inline void pkgActionItem::SetPrimary( pkgActionItem* ref )
332 selection[ to_install ] = ref->selection[ to_install ];
333 selection[ to_remove ] = ref->selection[ to_remove ];
336 pkgActionItem* pkgXmlDocument::Schedule
337 ( unsigned long action, pkgActionItem& item, pkgActionItem* rank )
339 /* Schedule an action item with a specified ranking order in
340 * the action list, (or at the end of the list if no ranking
341 * position is specified)...
343 pkgActionItem *ref = rank ? rank : actions;
345 /* If we already have a prior matching item...
347 pkgActionItem *prior;
348 if( (prior = actions->GetReference( item )) != NULL )
350 /* ...then, when the current request refers to a primary action,
351 * we update the already scheduled request to reflect this...
353 if( (action & ACTION_PRIMARY) == ACTION_PRIMARY )
354 prior->SetPrimary( rank = ref->Schedule( action & ACTION_MASK, item ) );
357 /* ...otherwise, when this request produces a valid package reference,
358 * we raise a new scheduling request...
360 else if( ((ref = ref->Schedule( action, item )) != NULL)
361 && ((ref->Selection() != NULL) || (ref->Selection( to_remove ) != NULL)) )
363 /* ...and, when successfully raised, add it to the task list...
367 * ...at the specified ranking position, if any...
369 return rank->Insert( ref );
372 /* ...otherwise, at the end of the list.
374 return actions = actions->Append( ref );
377 /* If we get to here, then no new action was scheduled; we simply
378 * return the current insertion point in the task list.
383 static __inline__ __attribute__((__always_inline__))
384 int reinstall_action_scheduled( pkgActionItem *package )
386 /* Helper function to identify scheduled actions which will
387 * result in reinstallation of the associated package.
390 ( pkgOptions()->Test( OPTION_REINSTALL )
391 && (package->Selection() == package->Selection( to_remove ))
395 void pkgActionItem::Execute()
399 pkgActionItem *current = this;
400 bool init_rites_pending = true;
401 while( current->prev != NULL ) current = current->prev;
403 /* Unless normal operations have been suppressed by the
404 * --print-uris option, (in order to obtain a list of all
405 * package URIs which the operation would access)...
407 if( pkgOptions()->Test( OPTION_PRINT_URIS ) < OPTION_PRINT_URIS )
409 /* ...we initiate any download requests which may
410 * be necessary to fetch all required archives into
411 * the local package cache.
413 DownloadArchiveFiles( current );
414 } while( SetAuthorities( current ) > 0 );
416 else while( current != NULL )
418 /* The --print-uris option is in effect: we simply loop
419 * over all packages with an assigned action, printing
420 * the associated download URI for each; (note that this
421 * will print the URI regardless of prior existence of
422 * the associated package in the local cache).
424 current->PrintURI( current->Selection()->ArchiveName() );
425 current = current->next;
428 /* If the --download-only option is in effect, then we have
429 * nothing more to do...
431 if( pkgOptions()->Test( OPTION_DOWNLOAD_ONLY ) != OPTION_DOWNLOAD_ONLY )
435 while( current != NULL )
437 /* ...processing only those packages with assigned actions...
439 if( (current->flags & ACTION_MASK) != 0 )
441 /* ...print a notification of the installation process to
442 * be performed, identifying the package to be processed.
445 pkgXmlNode *ref = current->Selection();
446 if( (tarname = ref->GetPropVal( tarname_key, NULL )) == NULL )
448 ref = current->Selection( to_remove );
449 tarname = ref->GetPropVal( tarname_key, value_unknown );
451 dmh_printf( "%s: %s\n", reinstall_action_scheduled( current )
452 ? "reinstall" : action_name( current->flags & ACTION_MASK ),
456 /* Package pre/post processing scripts may need to
457 * refer to the sysroot path for the package; place
458 * a copy in the environment, to facilitate this.
460 pkgSpecs lookup( tarname );
461 ref = ref->GetSysRoot( lookup.GetSubSystemName() );
462 const char *path = ref->GetPropVal( pathname_key, NULL );
465 /* Format the sysroot path into an environment variable
466 * assignment specification; note that the recorded path
467 * name is likely to include macros such as "%R", so we
468 * filter it through mkpath(), to expand them.
470 const char *nothing = "";
471 char varspec_template[9 + strlen( path )];
472 sprintf( varspec_template, "SYSROOT=%s", path );
473 char varspec[mkpath( NULL, varspec_template, nothing, NULL )];
474 mkpath( varspec, varspec_template, nothing, NULL );
475 pkgPutEnv( PKG_PUTENV_DIRSEP_MSW, varspec );
478 /* Check for any outstanding requirement to invoke the
479 * "self upgrade rites" process, so that we may install an
480 * upgrade for mingw-get itself...
482 if( init_rites_pending )
484 * ...discontinuing the check once this has been completed,
485 * since it need not be performed more than once.
487 init_rites_pending = self_upgrade_rites( tarname );
489 /* If we are performing an upgrade...
491 if( ((current->flags & ACTION_MASK) == ACTION_UPGRADE)
493 * ...and the latest version of the package is already installed...
495 && (current->Selection() == current->Selection( to_remove ))
497 * ...and the `--reinstall' option hasn't been specified...
499 && (pkgOptions()->Test( OPTION_REINSTALL ) == 0) )
501 * ...then simply report the up-to-date status...
503 dmh_notify( DMH_INFO, "package %s is up to date\n", tarname );
506 { /* ...otherwise, proceed to perform remove and install
507 * operations, as appropriate.
509 if( reinstall_action_scheduled( current )
510 || ((current->flags & ACTION_REMOVE) == ACTION_REMOVE) )
512 /* The selected package has been marked for removal, either
513 * explicitly, or as an implicit prerequisite for upgrade, or
514 * in preparation for reinstallation.
516 pkgRemove( current );
519 if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
521 /* The selected package has been marked for installation,
522 * either explicitly, or implicitly to complete a package upgrade.
524 pkgXmlNode *tmp = current->Selection( to_remove );
525 if( reinstall_action_scheduled( current )
526 || ((current->flags & ACTION_MASK) == ACTION_UPGRADE) )
527 current->selection[ to_remove ] = NULL;
528 pkgInstall( current );
529 current->selection[ to_remove ] = tmp;
533 /* Proceed to the next package, if any, with scheduled actions.
535 current = current->next;
541 pkgActionItem::~pkgActionItem()
544 * The package version range selectors, "min_wanted" and "max_wanted",
545 * are always allocated storage space on the heap; we need to free that,
546 * before we destroy the reference pointers.
548 if( (max_wanted != NULL) && (max_wanted != min_wanted) )
550 * "max_wanted" is non-NULL, and is distinct, (i.e. it doesn't
551 * represent an equality constraint which shares a reference with
552 * "min_wanted"); we need to free it independently.
554 free( (void *)(max_wanted) );
556 if( min_wanted != NULL )
558 * "min_wanted" is non-NULL; we don't care if it is distinct,
559 * because if not, freeing it is required anyway, to also free
560 * the same memory referenced by "max_wanted".
562 free( (void *)(min_wanted) );
568 * Implementation of processing hooks, for handling pre/post-install
569 * and pre/post-remove scripts.
574 static const char *action_key = "action";
575 static const char *normal_key = "normal";
577 int pkgXmlNode::DispatchScript
578 ( int status, const char *context, const char *priority, pkgXmlNode *action )
580 /* Private method, called by InvokeScript(), to hand-off each script
581 * fragment from the requesting XML node, with class attribute matching
582 * the requested context and precedence matching the requested priority,
583 * for execution by the embedded lua interpreter.
585 lua_State *interpreter = NULL;
586 static const char *priority_key = "precedence";
588 while( action != NULL )
590 /* We have at least one remaining script fragment, attached to the
591 * current XML node, which is a potential candidate for execution...
593 if( (strcmp( context, action->GetPropVal( class_key, value_none )) == 0)
594 && (strcmp( priority, action->GetPropVal( priority_key, normal_key )) == 0) )
596 /* ...and it does fit the current context and precedence; if we
597 * have not yet attached an interpreter to this node, then...
599 if( (interpreter == NULL) && ((interpreter = luaL_newstate()) != NULL) )
601 * ...start one now, and initialise it by loading the standard
604 luaL_openlibs( interpreter );
606 /* ...then hand off the current script fragment to this active
609 if( (status = luaL_dostring( interpreter, action->GetText() )) != 0 )
611 * ...reporting any errors through mingw-get's standard
612 * diagnostic message handler.
614 dmh_printf( "lua error in %s script:\n%s\n", context,
615 lua_tostring( interpreter, -1 )
619 /* Check for any further script fragments attached to the current node.
621 action = action->FindNextAssociate( action_key );
624 /* Before leaving this node...
626 if( interpreter != NULL )
628 * ...close any active lua interpreter which we may have attached.
630 lua_close( interpreter );
632 /* Finally, return the execution status reported by lua, from the last
633 * script fragment executed within the scope of the current node.
638 int pkgXmlNode::InvokeScript( int status, const char *context )
640 /* Private component of the implementation for the public
641 * InvokeScript() method; it checks for the existence of at
642 * least one script attached to the invoking XML node, then
643 * hands off processing of the entire script collection...
645 pkgXmlNode *action = FindFirstAssociate( action_key );
647 /* ...first processing any, in the requested context, which are
648 * designated as having "immediate" precedence...
650 status = DispatchScript( status, context, "immediate", action );
652 * ...then, traversing the XML hierarchy towards the root...
654 if( this != GetDocumentRoot() )
656 * ...processing any script fragments, in the requested context,
657 * which are attached to any container nodes...
659 status = GetParent()->InvokeScript( status, context );
661 /* ...and finally, process any others attached to the current node,
662 * in the requested context, having "normal" precedence.
664 return DispatchScript( status, context, normal_key, action );
667 /* $RCSfile$: end of file */