OSDN Git Service

Merge from mingw-get-0.1-mingw32-alpha-5 bug-fix branch.
[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, 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 "pkgproc.h"
35
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.
40  */
41 #define pkgOptionSelected( OPT )  OPT
42 #define PKG_OPTION_REINSTALL      true
43
44 EXTERN_C const char *action_name( unsigned long index )
45 {
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.
49    */
50   static const char* action_id[] =
51   {
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            */
56
57     "list",             /* list packages and display related information    */
58     "show",             /* a synonym for "list"                             */
59
60     "update"            /* update local copy of repository catalogues       */
61   };
62
63   /* For specified "index", return a pointer to the associated keyword,
64    * or NULL, if "index" is outside the defined action code range.
65    */
66   return ((index >= 0) && (index < end_of_actions))
67     ? action_id[ index ]
68     : NULL;
69 }
70
71 EXTERN_C int action_code( const char* request )
72 {
73   /* Match an action keyword specified on the command line
74    * to an entry from the above list...
75    */
76   int lencode = strlen( request );
77
78   int index;
79   for( index = 0; index < end_of_actions; index++ )
80   {
81     /* Try all defined keywords in turn, until we find a match
82      * or we run out of definitions...
83      */
84     if( strncmp( request, action_name( index ), lencode ) == 0 )
85       /*
86        * for a successful match...
87        * immediately return the associated action code index.
88        */
89       return index;
90   }
91
92   /* If we get to here, the specified keyword was not matched;
93    * signal this, by returning -1.
94    */
95   return -1;
96 }
97
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...
105  */
106 #define IMPLEMENT_INITIATION_RITES  PHASE_TWO_RITES
107 #include "rites.c"
108 /*
109  * ...until we know for sure that a self-upgrade has been scheduled...
110  */
111 RITES_INLINE bool self_upgrade_rites( const char *name )
112 {
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...
116    */
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);
121
122   if( ! pending )
123     /*
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...
127      */
128     invoke_rites();
129
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.
134    */
135   return pending;
136 }
137
138 pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before )
139 {
140   /* Construct an appropriately initialised non-specific pkgActionItem...
141    */
142   flags = 0;            /* no specific action yet assigned */
143
144   min_wanted = NULL;    /* no minimum package version constraint... */
145   max_wanted = NULL;    /* nor any maximum version */
146
147   /* Initialise package selection to NONE, for this action... */
148   selection[to_remove] = selection[to_install] = NULL;
149
150   /* Insert this item at a specified location in the actions list.
151    */
152   prev = after;
153   next = before;
154 }
155
156 pkgActionItem*
157 pkgActionItem::Append( pkgActionItem *item )
158 {
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.
165    */
166   if( this == NULL )
167     /*
168      * No list exists yet;
169      * return "item" as first and only entry in new list.
170      */
171     return item;
172
173   /* Ensure "item" physically exists, or if not, create a generic
174    * placeholder in which to construct it...
175    */
176   if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
177     /*
178      * ...bailing out if no such placeholder can be created.
179      */
180     return NULL;
181
182   /* Maintain list integrity...
183    */
184   if( (item->next = next) != NULL )
185     /*
186      * ...moving any existing items which already follow the insertion
187      * point in the list structure, to follow the newly added "item".
188      */
189     next->prev = item;
190
191   /* Set the new item's own reference pointer, to establish its list
192    * attachment point...
193    */
194   item->prev = this;
195
196   /* ...and attach it immediately after that point.
197    */
198   return next = item;
199 }
200
201 pkgActionItem*
202 pkgActionItem::Insert( pkgActionItem *item )
203 {
204   /* Add an "item" to an ActionItems list, inserting it immediately
205    * before the item referenced by the "this" pointer.
206    */
207   if( this == NULL )
208     /*
209      * No list exists yet;
210      * return "item" as first and only entry in new list.
211      */
212     return item;
213
214   /* Ensure "item" physically exists, or if not, create a generic
215    * placeholder in which to construct it...
216    */
217   if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
218     /*
219      * ...bailing out if no such placeholder can be created.
220      */
221     return NULL;
222
223   /* Maintain list integrity...
224    */
225   if( (item->prev = prev) != NULL )
226     /*
227      * ...moving any existing items which already precede the insertion
228      * point in the list structure, to precede the newly added "item".
229      */
230     prev->next = item;
231
232   /* Set the new item's own reference pointer, to establish the item
233    * currently at the attachment point, as its immediate successor...
234    */
235   item->next = this;
236
237   /* ...and attach it, immediately preceding that point.
238    */
239   return prev = item;
240 }
241
242 pkgActionItem*
243 pkgActionItem::Schedule( unsigned long action, pkgActionItem& item )
244 {
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.
248    */
249   pkgActionItem *rtn = new pkgActionItem(); *rtn = item;
250   rtn->flags = action | (rtn->flags & ~ACTION_MASK);
251   return rtn;
252 }
253
254 pkgActionItem*
255 pkgActionItem::GetReference( pkgActionItem& item )
256 {
257   /* Check for a prior reference, within the task schedule,
258    * for the package specified for processing by "item".
259    */
260   pkgXmlNode* pkg;
261   if( (pkg = item.Selection()->GetParent()) != NULL )
262   {
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
266      * the schedule...
267      */
268     for( pkgActionItem* item = this; item != NULL; item = item->prev )
269     {
270       /* ...and if we find another item holding an identical pointer,
271        * (i.e. to the same package), we return it...
272        */
273       if( item->Selection()->GetParent() == pkg )
274         return item;
275     }
276   }
277
278   /* If we get to here, there is no prior action scheduled for the
279    * specified package, so we return a NULL pointer...
280    */
281   return NULL;
282 }
283
284 pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
285 {
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.
289    */
290   pkgSpecs test( package );
291
292   /* Establish the selection criteria...
293    */
294   pkgSpecs min_fit( min_wanted );
295   pkgSpecs max_fit( max_wanted );
296
297   /* Choose one of the above, as a basis for identification of
298    * a correct package-component match...
299    */
300   pkgSpecs& fit = min_wanted ? min_fit : max_fit;
301
302   /* Verify that "package" fulfills the selection criteria...
303    */
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)))  )
308   {
309     /* We have the correct package component, and it fits within
310      * the allowed range of release versions...
311      */
312     pkgSpecs last( Selection() );
313     if( test > last )
314       /*
315        * It is also more recent than the current selection,
316        * so we now replace that...
317        */
318       selection[to_install] = package;
319   }
320
321   /* Whatever choice we make, we return the resultant selection...
322    */
323   return Selection();
324 }
325
326 pkgActionItem* pkgXmlDocument::Schedule
327 ( unsigned long action, pkgActionItem& item, pkgActionItem* rank )
328 {
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)...
332    */
333   pkgActionItem *ref = rank ? rank : actions;
334
335   /* Don't reschedule, if we already have a prior matching item...
336    */
337   if(  (actions->GetReference( item ) == NULL)
338   /*
339    * ...but, when we don't, and when this request produces a valid
340    * package reference, we raise a new scheduling request...
341    */
342   &&  ((ref = ref->Schedule( action, item )) != NULL)
343   &&  ((ref->Selection() != NULL) || (ref->Selection( to_remove ) != NULL)) )
344   {
345     /* ...and, when successfully raised, add it to the task list...
346      */
347     if( rank )
348       /*
349        * ...at the specified ranking position, if any...
350        */
351       return rank->Insert( ref );
352
353     else
354       /* ...otherwise, at the end of the list.
355        */
356       return actions = actions->Append( ref );
357   }
358
359   /* If we get to here, then no new action was scheduled; we simply
360    * return the current insertion point in the task list.
361    */
362   return rank;
363 }
364
365 void pkgActionItem::Execute()
366 {
367   if( this != NULL )
368   {
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 )
374     {
375       /* Print a notification of the installation process to be
376        * performed, identifying the package to be processed.
377        */
378       const char *tarname;
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 );
382
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...
386        */
387       if( init_rites_pending )
388         /*
389          * ...discontinuing the check once this has been completed,
390          * since it need not be performed more than once.
391          */
392         init_rites_pending = self_upgrade_rites( tarname );
393
394       if( (current->flags & ACTION_REMOVE) == ACTION_REMOVE )
395       {
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.
399          *
400          * FIXME: This implementation is a stub, to be rewritten when the system
401          * manifest structure has been specified and implemented.
402          */
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 )
406             );
407       }
408
409       if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
410       {
411         /* The selected package has been marked for installation, either explicitly,
412          * or implicitly to complete a package upgrade.
413          */
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;
419       }
420
421       /* Proceed to next package with scheduled actions.
422        */
423       current = current->next;
424     }
425   }
426 }
427
428 pkgActionItem::~pkgActionItem()
429 {
430   /* Destructor...
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.
434    */
435   if( (max_wanted != NULL) && (max_wanted != min_wanted) )
436     /*
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.
440      */
441     free( (void *)(max_wanted) );
442
443   if( min_wanted != NULL )
444     /*
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".
448      */
449     free( (void *)(min_wanted) );
450 }
451
452 /* $RCSfile$: end of file */