OSDN Git Service

e3229e75c2f69299ff2c82a8ed7806a92e53c5d6
[mingw/mingw-get.git] / src / pkgexec.cpp
1 /*
2  * pkgexec.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, MinGW Project
8  *
9  *
10  * Implementation of package management task scheduler and executive.
11  *
12  *
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.
18  *
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.
25  *
26  */
27 #include "dmh.h"
28 #include "mkpath.h"
29
30 #include "pkgbase.h"
31 #include "pkgkeys.h"
32 #include "pkginfo.h"
33 #include "pkgtask.h"
34 #include "pkgopts.h"
35 #include "pkgproc.h"
36
37 EXTERN_C const char *action_name( unsigned long index )
38 {
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.
42    */
43   static const char* action_id[] =
44   {
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            */
49
50     "list",             /* list packages and display related information    */
51     "show",             /* a synonym for "list"                             */
52
53     "update",           /* update local copy of repository catalogues       */
54     "licence",          /* retrieve licence sources from repository         */
55     "source"            /* retrieve package sources from repository         */
56   };
57
58   /* For specified "index", return a pointer to the associated keyword,
59    * or NULL, if "index" is outside the defined action code range.
60    */
61   return ((index >= 0) && (index < end_of_actions))
62     ? action_id[ index ]
63     : NULL;
64 }
65
66 EXTERN_C int action_code( const char* request )
67 {
68   /* Match an action keyword specified on the command line
69    * to an entry from the above list...
70    */
71   int lencode = strlen( request );
72
73   int index;
74   for( index = 0; index < end_of_actions; index++ )
75   {
76     /* Try all defined keywords in turn, until we find a match
77      * or we run out of definitions...
78      */
79     if( strncmp( request, action_name( index ), lencode ) == 0 )
80       /*
81        * for a successful match...
82        * immediately return the associated action code index.
83        */
84       return index;
85   }
86
87   /* If we get to here, the specified keyword was not matched;
88    * signal this, by returning -1.
89    */
90   return -1;
91 }
92
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...
100  */
101 #define IMPLEMENT_INITIATION_RITES  PHASE_TWO_RITES
102 #include "rites.c"
103 /*
104  * ...until we know for sure that a self-upgrade has been scheduled...
105  */
106 RITES_INLINE bool self_upgrade_rites( const char *name )
107 {
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...
111    */
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);
116
117   if( ! pending )
118     /*
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...
122      */
123     invoke_rites();
124
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.
129    */
130   return pending;
131 }
132
133 pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before )
134 {
135   /* Construct an appropriately initialised non-specific pkgActionItem...
136    */
137   flags = 0;            /* no specific action yet assigned */
138
139   min_wanted = NULL;    /* no minimum package version constraint... */
140   max_wanted = NULL;    /* nor any maximum version */
141
142   /* Initialise package selection to NONE, for this action... */
143   selection[to_remove] = selection[to_install] = NULL;
144
145   /* Insert this item at a specified location in the actions list.
146    */
147   prev = after;
148   next = before;
149 }
150
151 pkgActionItem*
152 pkgActionItem::Append( pkgActionItem *item )
153 {
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.
160    */
161   if( this == NULL )
162     /*
163      * No list exists yet;
164      * return "item" as first and only entry in new list.
165      */
166     return item;
167
168   /* Ensure "item" physically exists, or if not, create a generic
169    * placeholder in which to construct it...
170    */
171   if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
172     /*
173      * ...bailing out if no such placeholder can be created.
174      */
175     return NULL;
176
177   /* Maintain list integrity...
178    */
179   if( (item->next = next) != NULL )
180     /*
181      * ...moving any existing items which already follow the insertion
182      * point in the list structure, to follow the newly added "item".
183      */
184     next->prev = item;
185
186   /* Set the new item's own reference pointer, to establish its list
187    * attachment point...
188    */
189   item->prev = this;
190
191   /* ...and attach it immediately after that point.
192    */
193   return next = item;
194 }
195
196 pkgActionItem*
197 pkgActionItem::Insert( pkgActionItem *item )
198 {
199   /* Add an "item" to an ActionItems list, inserting it immediately
200    * before the item referenced by the "this" pointer.
201    */
202   if( this == NULL )
203     /*
204      * No list exists yet;
205      * return "item" as first and only entry in new list.
206      */
207     return item;
208
209   /* Ensure "item" physically exists, or if not, create a generic
210    * placeholder in which to construct it...
211    */
212   if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
213     /*
214      * ...bailing out if no such placeholder can be created.
215      */
216     return NULL;
217
218   /* Maintain list integrity...
219    */
220   if( (item->prev = prev) != NULL )
221     /*
222      * ...moving any existing items which already precede the insertion
223      * point in the list structure, to precede the newly added "item".
224      */
225     prev->next = item;
226
227   /* Set the new item's own reference pointer, to establish the item
228    * currently at the attachment point, as its immediate successor...
229    */
230   item->next = this;
231
232   /* ...and attach it, immediately preceding that point.
233    */
234   return prev = item;
235 }
236
237 pkgActionItem*
238 pkgActionItem::Schedule( unsigned long action, pkgActionItem& item )
239 {
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.
243    */
244   pkgActionItem *rtn = new pkgActionItem(); *rtn = item;
245   if( pkgOptions()->Test( OPTION_REINSTALL ) == OPTION_REINSTALL )
246     /*
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.
251      */
252     action |= ACTION_DOWNLOAD;
253   rtn->flags = action | (rtn->flags & ~ACTION_MASK);
254   return rtn;
255 }
256
257 pkgActionItem*
258 pkgActionItem::GetReference( pkgActionItem& item )
259 {
260   /* Check for a prior reference, within the task schedule,
261    * for the package specified for processing by "item".
262    */
263   pkgXmlNode* pkg;
264   if( (pkg = item.Selection()->GetParent()) != NULL )
265   {
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
269      * the schedule...
270      */
271     for( pkgActionItem* item = this; item != NULL; item = item->prev )
272     {
273       /* ...and if we find another item holding an identical pointer,
274        * (i.e. to the same package), we return it...
275        */
276       if( item->Selection()->GetParent() == pkg )
277         return item;
278     }
279   }
280
281   /* If we get to here, there is no prior action scheduled for the
282    * specified package, so we return a NULL pointer...
283    */
284   return NULL;
285 }
286
287 pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
288 {
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.
292    */
293   pkgSpecs test( package );
294
295   /* Establish the selection criteria...
296    */
297   pkgSpecs min_fit( min_wanted );
298   pkgSpecs max_fit( max_wanted );
299
300   /* Choose one of the above, as a basis for identification of
301    * a correct package-component match...
302    */
303   pkgSpecs& fit = min_wanted ? min_fit : max_fit;
304
305   /* Verify that "package" fulfills the selection criteria...
306    */
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)))  )
311   {
312     /* We have the correct package component, and it fits within
313      * the allowed range of release versions...
314      */
315     pkgSpecs last( Selection() );
316     if( test > last )
317       /*
318        * It is also more recent than the current selection,
319        * so we now replace that...
320        */
321       selection[to_install] = package;
322   }
323
324   /* Whatever choice we make, we return the resultant selection...
325    */
326   return Selection();
327 }
328
329 inline void pkgActionItem::SetPrimary( pkgActionItem* ref )
330 {
331   flags = ref->flags;
332   selection[ to_install ] = ref->selection[ to_install ];
333   selection[ to_remove ] = ref->selection[ to_remove ];
334 }
335
336 pkgActionItem* pkgXmlDocument::Schedule
337 ( unsigned long action, pkgActionItem& item, pkgActionItem* rank )
338 {
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)...
342    */
343   pkgActionItem *ref = rank ? rank : actions;
344
345   /* If we already have a prior matching item...
346    */
347   pkgActionItem *prior;
348   if( (prior = actions->GetReference( item )) != NULL )
349   {
350     /* ...then, when the current request refers to a primary action,
351      * we update the already scheduled request to reflect this...
352      */
353     if( (action & ACTION_PRIMARY) == ACTION_PRIMARY )
354       prior->SetPrimary( rank = ref->Schedule( action & ACTION_MASK, item ) );
355     return prior;
356   }
357   /* ...otherwise, when this request produces a valid package reference,
358    * we raise a new scheduling request...
359    */
360   else if( ((ref = ref->Schedule( action, item )) != NULL)
361   &&   ((ref->Selection() != NULL) || (ref->Selection( to_remove ) != NULL)) )
362   {
363     /* ...and, when successfully raised, add it to the task list...
364      */
365     if( rank )
366       /*
367        * ...at the specified ranking position, if any...
368        */
369       return rank->Insert( ref );
370
371     else
372       /* ...otherwise, at the end of the list.
373        */
374       return actions = actions->Append( ref );
375   }
376
377   /* If we get to here, then no new action was scheduled; we simply
378    * return the current insertion point in the task list.
379    */
380   return rank;
381 }
382
383 static __inline__ __attribute__((__always_inline__))
384 int reinstall_action_scheduled( pkgActionItem *package )
385 {
386   /* Helper function to identify scheduled actions which will
387    * result in reinstallation of the associated package.
388    */
389   return
390     ( pkgOptions()->Test( OPTION_REINSTALL )
391       && (package->Selection() == package->Selection( to_remove ))
392     );
393 }
394
395 void pkgActionItem::Execute()
396 {
397   if( this != NULL )
398   {
399     pkgActionItem *current = this;
400     bool init_rites_pending = true;
401     while( current->prev != NULL ) current = current->prev;
402
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)...
406      */
407     if( pkgOptions()->Test( OPTION_PRINT_URIS ) < OPTION_PRINT_URIS )
408       do {
409            /* ...we initiate any download requests which may
410             * be necessary to fetch all required archives into
411             * the local package cache.
412             */
413            DownloadArchiveFiles( current );
414          } while( SetAuthorities( current ) > 0 );
415
416     else while( current != NULL )
417     {
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).
423        */
424       current->PrintURI( current->Selection()->ArchiveName() );
425       current = current->next;
426     }
427
428     /* If the --download-only option is in effect, then we have
429      * nothing more to do...
430      */
431     if( pkgOptions()->Test( OPTION_DOWNLOAD_ONLY ) != OPTION_DOWNLOAD_ONLY )
432     {
433       /* ...otherwise...
434        */
435       while( current != NULL )
436       {
437         /* ...processing only those packages with assigned actions...
438          */
439         if( (current->flags & ACTION_MASK) != 0 )
440         {
441           /* ...print a notification of the installation process to
442            * be performed, identifying the package to be processed.
443            */
444           const char *tarname;
445           pkgXmlNode *ref = current->Selection();
446           if( (tarname = ref->GetPropVal( tarname_key, NULL )) == NULL )
447           {
448             ref = current->Selection( to_remove );
449             tarname = ref->GetPropVal( tarname_key, value_unknown );
450           }
451           dmh_printf( "%s: %s\n", reinstall_action_scheduled( current )
452               ? "reinstall" : action_name( current->flags & ACTION_MASK ),
453               tarname
454             );
455
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.
459            */
460           pkgSpecs lookup( tarname );
461           ref = ref->GetSysRoot( lookup.GetSubSystemName() );
462           const char *path = ref->GetPropVal( pathname_key, NULL );
463           if( path != NULL )
464           {
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.
469              */
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 );
476           }
477
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...
481            */
482           if( init_rites_pending )
483             /*
484              * ...discontinuing the check once this has been completed,
485              * since it need not be performed more than once.
486              */
487             init_rites_pending = self_upgrade_rites( tarname );
488
489           /* If we are performing an upgrade...
490            */
491           if( ((current->flags & ACTION_MASK) == ACTION_UPGRADE)
492           /*
493            * ...and the latest version of the package is already installed...
494            */
495           &&  (current->Selection() == current->Selection( to_remove ))
496           /*
497            * ...and the `--reinstall' option hasn't been specified...
498            */
499           &&  (pkgOptions()->Test( OPTION_REINSTALL ) == 0)  )
500             /*
501              * ...then simply report the up-to-date status...
502              */
503             dmh_notify( DMH_INFO, "package %s is up to date\n", tarname );
504
505           else
506           { /* ...otherwise, proceed to perform remove and install
507              * operations, as appropriate.
508              */
509             if(   reinstall_action_scheduled( current )
510             ||  ((current->flags & ACTION_REMOVE) == ACTION_REMOVE)  )
511             {
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.
515                */
516               pkgRemove( current );
517             }
518
519             if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
520             {
521               /* The selected package has been marked for installation,
522                * either explicitly, or implicitly to complete a package upgrade.
523                */
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;
530             }
531           }
532         }
533         /* Proceed to the next package, if any, with scheduled actions.
534          */
535         current = current->next;
536       }
537     }
538   }
539 }
540
541 pkgActionItem::~pkgActionItem()
542 {
543   /* Destructor...
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.
547    */
548   if( (max_wanted != NULL) && (max_wanted != min_wanted) )
549     /*
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.
553      */
554     free( (void *)(max_wanted) );
555
556   if( min_wanted != NULL )
557     /*
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".
561      */
562     free( (void *)(min_wanted) );
563 }
564
565 /*
566  ****************
567  *
568  * Implementation of processing hooks, for handling pre/post-install
569  * and pre/post-remove scripts.
570  *
571  */
572 #include "lua.hpp"
573
574 static const char *action_key = "action";
575 static const char *normal_key = "normal";
576
577 int pkgXmlNode::DispatchScript
578 ( int status, const char *context, const char *priority, pkgXmlNode *action )
579 {
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.
584    */
585   lua_State *interpreter = NULL;
586   static const char *priority_key = "precedence";
587
588   while( action != NULL )
589   {
590     /* We have at least one remaining script fragment, attached to the
591      * current XML node, which is a potential candidate for execution...
592      */
593     if( (strcmp( context, action->GetPropVal( class_key, value_none )) == 0)
594     &&  (strcmp( priority, action->GetPropVal( priority_key, normal_key )) == 0)  )
595     {
596       /* ...and it does fit the current context and precedence; if we
597        * have not yet attached an interpreter to this node, then...
598        */
599       if( (interpreter == NULL) && ((interpreter = luaL_newstate()) != NULL) )
600         /*
601          * ...start one now, and initialise it by loading the standard
602          * lua libraries...
603          */
604         luaL_openlibs( interpreter );
605
606       /* ...then hand off the current script fragment to this active
607        * lua interpreter...
608        */
609       if( (status = luaL_dostring( interpreter, action->GetText() )) != 0 )
610         /*
611          * ...reporting any errors through mingw-get's standard
612          * diagnostic message handler.
613          */
614         dmh_printf( "lua error in %s script:\n%s\n", context,
615             lua_tostring( interpreter, -1 )
616           );
617     }
618
619     /* Check for any further script fragments attached to the current node.
620      */
621     action = action->FindNextAssociate( action_key );
622   }
623
624   /* Before leaving this node...
625    */
626   if( interpreter != NULL )
627     /*
628      * ...close any active lua interpreter which we may have attached.
629      */
630     lua_close( interpreter );
631
632   /* Finally, return the execution status reported by lua, from the last
633    * script fragment executed within the scope of the current node.
634    */
635   return status;
636 }
637
638 int pkgXmlNode::InvokeScript( int status, const char *context )
639 {
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...
644    */
645   pkgXmlNode *action = FindFirstAssociate( action_key );
646
647   /* ...first processing any, in the requested context, which are
648    * designated as having "immediate" precedence...
649    */
650   status = DispatchScript( status, context, "immediate", action );
651   /*
652    * ...then, traversing the XML hierarchy towards the root...
653    */
654   if( this != GetDocumentRoot() )
655     /*
656      * ...processing any script fragments, in the requested context,
657      * which are attached to any container nodes...
658      */
659     status = GetParent()->InvokeScript( status, context );
660
661   /* ...and finally, process any others attached to the current node,
662    * in the requested context, having "normal" precedence.
663    */
664   return DispatchScript( status, context, normal_key, action );
665 }
666
667 /* $RCSfile$: end of file */