OSDN Git Service

Support group affiliation with component package granularity.
[mingw/mingw-get.git] / src / rites.c
1 #ifndef BEGIN_RITES_IMPLEMENTATION
2 /*
3  * rites.c
4  *
5  * $Id$
6  *
7  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
8  * Copyright (C) 2009, 2010, 2012, 2013, MinGW.org Project
9  *
10  *
11  * Implementation of the main program function for the "lastrites.exe"
12  * helper application; also, when included within another compilation
13  * module, which pre-defines IMPLEMENT_INITIATION_RITES, it furnishes
14  * the complementary "pkgInitRites()" and "pkgLastRites()" functions.
15  *
16  * The combination of a call to pkgInitRites() at program start-up,
17  * followed by eventual termination by means of an "execl()" call to
18  * invoke "lastrites.exe", equips "mingw-get" with the capability to
19  * work around the MS-Windows limitation which prohibits replacement
20  * of the image files for a running application, and so enables the
21  * "mingw-get" application to upgrade itself.
22  *
23  *
24  * This is free software.  Permission is granted to copy, modify and
25  * redistribute this software, under the provisions of the GNU General
26  * Public License, Version 3, (or, at your option, any later version),
27  * as published by the Free Software Foundation; see the file COPYING
28  * for licensing details.
29  *
30  * Note, in particular, that this software is provided "as is", in the
31  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
32  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
33  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
34  * MinGW Project, accept liability for any damages, however caused,
35  * arising from the use of this software.
36  *
37  */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41
42 /* Path names for supplementary files.  Note that these are relative
43  * to the root of the mingw-get directory tree, as identified by the
44  * $APPROOT environment variable.
45  */
46 #define MINGW_GET_EXE   L"bin/mingw-get.exe"
47 #define MINGW_GET_LCK   L"var/lib/mingw-get/lock"
48 #define MINGW_GET_DLL   L"libexec/mingw-get/mingw-get-0.dll"
49 #define MINGW_GET_GUI   L"libexec/mingw-get/guimain.exe"
50 #define MINGW_GET_GFB   L"libexec/mingw-get/guistub.exe"
51
52 /* We wish to define a number of helper functions, which we will prefer
53  * to always compile to inline code; this convenience macro may be used
54  * to facilitate achievement of this objective.
55  */
56 #define RITES_INLINE    static __inline__ __attribute__((__always_inline__))
57
58 #ifndef EOK
59  /*
60   * Typically, errno.h does not define a code for "no error", but
61   * it's convenient for us to have one, so that we may clear "errno"
62   * to ensure that we don't inadvertently react to a stale value.
63   */
64 # define EOK            0
65 #endif
66
67 #ifdef IMPLEMENT_INITIATION_RITES
68  /*
69   * Normally specified by including this source file within another,
70   * with an appropriate prior definition.  This indicates intent to
71   * provide an inline implementation of either first or second phase
72   * initiation rites, (mutually exclusively), depending on whether
73   * IMPLEMENT_INITIATION_RITES is defined as PHASE_ONE_RITES, or as
74   * PHASE_TWO_RITES; in either case, the associated code is invoked
75   * via the locally implemented "invoke_rites()" function.
76   */
77 # define BEGIN_RITES_IMPLEMENTATION     RITES_INLINE void invoke_rites( void )
78  /*
79   * In this case, since the "invoke_rites()" function has nothing
80   * to return, we declare a "do nothing" wrap-up hook.
81   */
82 # define END_RITES_IMPLEMENTATION
83
84 #else /* ! defined IMPLEMENT_INITIATION_RITES */
85  /*
86   * This alternative case is normally achieved by free-standing
87   * compilation of rites.c; it implements the "main()" function
88   * for the "lastrites.exe" helper program.
89   */
90 # define BEGIN_RITES_IMPLEMENTATION     int main()
91  /*
92   * In this case, we must return an exit code; we simply assume that,
93   * for us. there is no meaningful concept of process failure, so we
94   * always report success.
95   */
96 # define END_RITES_IMPLEMENTATION       return EXIT_SUCCESS;
97
98 /* We may note that this free-standing "main()" function doesn't
99  * process command line arguments, so we may disable globbing in
100  * the system runtime start-up code.
101  */
102 int _CRT_glob = 0;
103 #endif
104
105 /* Provide selectors, to discriminate the two distinct classes
106  * of initiation rites implementation, which may be specified as
107  * the requisite form with IMPLEMENT_INITIATION_RITES.
108  */
109 #define PHASE_ONE_RITES  1
110 #define PHASE_TWO_RITES  2
111
112 #if IMPLEMENT_INITIATION_RITES == PHASE_TWO_RITES
113  /*
114   * For the second phase initiation rites implementation, the
115   * idea is to move the running executable and its associated DLL
116   * out of the way, so that we may install upgraded versions while
117   * the application is still running.  Thus, the first step is to
118   * destroy any previously created backup copies of the running
119   * program files which may still exist...
120   */
121 # define first_act( SOURCE, BACKUP )    mingw_get_unlink( (BACKUP) )
122  /*
123   * ...then, we schedule a potential pending removal of these, by
124   * initially renaming them with their designated backup names...
125   */
126 # define final_act( SOURCE, BACKUP )    mingw_get_rename( (SOURCE), (BACKUP) )
127
128 #else /* ! PHASE_TWO_RITES */
129  /*
130   * In this case, we assume that the originally running process
131   * may have invoked phase two initiation rites, so moving its own
132   * executable and its associated DLL out of the way; we aim to move
133   * them back again, by attempting to change the backup names back
134   * to their original file names...
135   */
136 # define first_act( SOURCE, BACKUP )    mingw_get_rename( (BACKUP), (SOURCE) )
137  /*
138   * We expect the preceding "rename" to succeed; if it didn't, then
139   * the most probable reason is that an upgrade has been installed,
140   * in which case we may remove the obsolete backup versions.
141   */
142 # define final_act( SOURCE, BACKUP )    mingw_get_remove( (BACKUP) )
143 #endif
144
145 RITES_INLINE const char *approot_path( void )
146 {
147   /* Inline helper to identify the root directory path for the running
148    * application, (which "mingw-get" passes through the APPROOT variable
149    * in the process environment)...
150    *
151    * Caution: although this is called more than once, DO NOT attempt to
152    * optimise getenv() lookup by saving the returned pointer across calls;
153    * the environment block may have been moved between calls, which makes
154    * the pointer returned from a previous call potentially invalid!
155    */
156   char *approot;
157   return ((approot = getenv( "APPROOT" )) == NULL)
158
159     ? "c:\\mingw\\"     /* default, for failed environment look-up */
160     : approot;          /* normal return value */
161 }
162
163 #include "debug.h"
164
165 #if DEBUGLEVEL & DEBUG_INHIBIT_RITES_OF_PASSAGE
166  /*
167   * When debugging, (with rites of passage selected for debugging),
168   * then we substitute debug message handlers for the real "rename()"
169   * and "unlink()" functions used to implement the rites.
170   */
171 RITES_INLINE int mingw_get_rename( const char *from, const char *to )
172 {
173   /* Inline debugging message handler to report requests to perform
174    * the file rename rite, with optional success/failure result.
175    */
176   fprintf( stderr, "rename: %s to %s\n", from, to );
177   errno = (DEBUGLEVEL & DEBUG_FAIL_FILE_RENAME_RITE) ? EEXIST : EOK;
178   return (DEBUGLEVEL & DEBUG_FAIL_FILE_RENAME_RITE) ? -1 : 0;
179 }
180
181 RITES_INLINE int mingw_get_unlink( const char *name )
182 {
183   /* Inline debugging message handler to report requests to perform
184    * the file "unlink" rite.
185    */
186   fprintf( stderr, "unlink: %s\n", name );
187   return (DEBUGLEVEL & DEBUG_FAIL_FILE_UNLINK_RITE) ? -1 : 0;
188 }
189
190 #else
191  /* We are doing it for real...
192   * Simply map the "rename" and "unlink" requests through to the
193   * appropriate system calls.
194   */
195 # define mingw_get_rename( N1, N2 )     rename( (N1), (N2) )
196 # define mingw_get_unlink( N1 )         unlink( (N1) )
197 #endif
198
199 RITES_INLINE void mingw_get_remove( const char *name )
200 {
201   /* Inline helper to perform the "unlink" rite, provided a preceding
202    * request, (presumably "rename"), has not failed with EEXIST status.
203    */
204   if( errno == EEXIST ) mingw_get_unlink( name );
205 }
206
207 RITES_INLINE void perform_rites_of_passage( const wchar_t *name )
208 {
209   /* Local helper function, to perform the required rite of passage
210    * for a single specified process image file, as specified by its
211    * relative path name within the application directory tree.
212    */
213   const char *approot = approot_path();
214
215   /* We begin by allocating stack space for the absolute path names
216    * for both the original file name and its backup name.
217    */
218   size_t buflen = snprintf( NULL, 0, "%s%S~", approot, name );
219   char normal_name[ buflen ], backup_name[ 1 + buflen ];
220
221   /* Fill out this buffer pair with the requisite path names,
222    * noting that the backup name is the same as the original
223    * name, with a single tilde appended.
224    */
225   snprintf( normal_name, buflen, "%s%S", approot, name );
226   snprintf( backup_name, ++buflen, "%s~", normal_name );
227
228   /* Clear any pre-existing error condition, then perform the
229    * requisite "rename" and "unlink" rites, in the pre-scheduled
230    * order appropriate to the build context.
231    */
232   errno = EOK;
233   first_act( normal_name, backup_name );
234   final_act( normal_name, backup_name );
235 }
236
237 /* Here, we specify the variant portion of the implementation...
238  */
239 BEGIN_RITES_IMPLEMENTATION
240 {
241   /* ...where the requisite "rites of passage" are initiated
242    * for each process image file affected, specifying each by
243    * its path name relative to the root of the application's
244    * installation directory tree.
245    */
246   perform_rites_of_passage( MINGW_GET_EXE );
247   perform_rites_of_passage( MINGW_GET_DLL );
248   perform_rites_of_passage( MINGW_GET_GUI );
249   END_RITES_IMPLEMENTATION
250 }
251
252 #if IMPLEMENT_INITIATION_RITES == PHASE_ONE_RITES
253 /*
254  * The following inline functions are required, specifically
255  * and exclusively, for the first phase of initiation rites...
256  */
257 # include <process.h>
258 # include <sys/types.h>
259 # include <sys/stat.h>
260 # include <fcntl.h>
261
262 RITES_INLINE const char *lockfile_name( void )
263 {
264   /* Helper to identify the absolute path for the lock file...
265    */
266   static char *lockfile = NULL;
267
268   if( lockfile == NULL )
269   {
270     /* We resolve this only once; this is the first reference,
271      * (or all prior references were unsuccessfully resolved), so
272      * we must resolve it now.
273      */
274     const char *lockpath = approot_path();
275     const wchar_t *lockname = MINGW_GET_LCK;
276     size_t wanted = 1 + snprintf( NULL, 0, "%s%S", lockpath, lockname );
277     if( (lockfile = (char *)(malloc( wanted ))) != NULL )
278       snprintf( lockfile, wanted, "%s%S", lockpath, lockname );
279   }
280   /* In any case, we return the pointer as resolved on first call.
281    */
282   return lockfile;
283 }
284
285 /* Provide a facility for clearing a stale lock; for Win32, we may
286  * simply refer this to the "unlink()" function, because the system
287  * will not permit us to unlink a lock file which is owned by any
288  * active process; (i.e. it is NOT a stale lock).
289  *
290  * FIXME: This will NOT work on Linux (or other Unixes), where it
291  *        IS permitted to unlink an active lock file; to support
292  *        such systems, we will need to provide a more robust
293  *        implementation for "unlink_if_stale()".
294  */
295 #define unlink_if_stale  mingw_get_unlink
296
297 #ifdef GUIMAIN_H
298  /*
299   * When guimain.h has been included, we infer that we are compiling
300   * inline components for the mingw-get GUI application; we implement
301   * custom variants of some sub-components which are GUI specific.
302   */
303   RITES_INLINE void pkgLockFail( const char *progname, const char *lockfile )
304   {
305     /* Helper function to diagnose failure to acquire the package lock.
306      * This implementation is for use in the GUI application, where there
307      * may be no stderr stream attached; construct a suitable message to
308      * be displayed in a windows message box.
309      */
310     const char *fmt = "*** ERROR *** cannot acquire catalogue lock\n%s: %s";
311     char msg[1 + snprintf( NULL, 0, fmt, lockfile, strerror( errno ) )];
312     snprintf( msg, sizeof( msg ), fmt, lockfile, strerror( errno ) );
313     MessageBox( NULL, msg, progname, MB_ICONERROR );
314   }
315
316 #else
317  /* When guimain.h has NOT been included, and we are compiling inline,
318   * we fall back to implementing CLI specific sub-component variants.
319   */
320   RITES_INLINE void pkgLockFail( const char *progname, const char *lockfile )
321   {
322     /* Helper function to diagnose failure to acquire the package lock.
323      * This implementation is for use in the CLI application, which should
324      * always have a stderr stream attached; emit a suitable sequence of
325      * messages by simply writing to this stream.
326      */
327     fprintf( stderr,
328         "%s: cannot acquire lock for exclusive execution\n", progname
329       );
330     fprintf( stderr, "%s: ", progname ); perror( lockfile );
331     if( errno == EEXIST )
332       fprintf( stderr, "%s: another mingw-get process appears to be running\n",
333           progname
334         );
335   }
336 #endif
337
338 RITES_INLINE int pkgInitRites( const char *progname )
339 {
340   /* Helper to acquire an exclusive execution lock, and if sucessful,
341    * to establish pre-conditions to permit self-upgrade.
342    */
343   int lock;
344   const char *lockfile;
345
346   /* First, attempt to clear any prior (stale) lock, then create
347    * a new one.  (Note that we DON'T use O_TEMPORARY here; on Win2K,
348    * it leads to strange behaviour when another process attempts to
349    * unlink a stale lock file).
350    */
351   unlink_if_stale( lockfile = lockfile_name() );
352   if( (lock = open( lockfile, O_RDWR | O_CREAT | O_EXCL, S_IWRITE )) < 0 )
353   {
354     /* We failed to acquire the lock; diagnose failure...
355      */
356     pkgLockFail( progname, lockfile );
357   }
358
359   /* Return the lock, indicating success or failure as appropriate.
360    */
361   return lock;
362 }
363
364 RITES_INLINE int pkgLastRites( int lock, const char *progname )
365 {
366   /* Inline helper to clear the lock acquired by "pkgInitRites()",
367    * and to initiate clean-up of the changes made by "invoke_rites()"
368    * when it is invoked in second phase of initiation rites.
369    */
370   const char *approot = approot_path();
371   const char *lastrites = "libexec/mingw-get/lastrites.exe";
372   char rites[1 + snprintf( NULL, 0, "%s%s", approot, lastrites )];
373
374   /* Clear the lock; note that we must both close AND unlink the
375    * lock file, because we didn't open it as O_TEMPORARY.
376    */
377   close( lock );
378   unlink( lockfile_name() );
379
380   /* Initiate clean-up; we hand this off to a free-standing process,
381    * so that it may delete the old EXE and DLL image files belonging to
382    * this process, if they were upgraded since acquiring the lock.
383    *
384    * Note that we use the execl() function to invoke the clean-up
385    * process.  However, we recognise that Microsoft's implementation
386    * of this function does NOT behave in a POSIXly correct manner;
387    * specifically, it returns control immediately to the calling
388    * process, causing it to resume execution concurrently with the
389    * exec()ed process, whereas POSIXly correct behaviour would cause
390    * the calling process to wait for the exec()ed process.  This
391    * lack of POSIX-like behaviour is unfortunate, since it results
392    * in a potential race condition between the exec()ed process and
393    * the calling process, should the latter immediately attempt to
394    * invoke a new instance of mingw-get.  To mitigate this potential
395    * race condition, we call the "invoke_rites()" function to pre-empt
396    * as much as possible of the processing to be performed by the
397    * clean-up program, recognising that we can be only partially
398    * successful, (but silently ignoring the partial failure), before
399    * calling execl() to complete those clean-up aspects which cannot
400    * be successfully performed in this pre-emptive fashion.
401    */
402   snprintf( rites, sizeof( rites ), "%s%s", approot, lastrites );
403   invoke_rites(); execl( rites, "lastrites", NULL );
404
405   /* We should never get to here; if we do...
406    * Diagnose a problem, and bail out.
407    */
408   fprintf( stderr, "%s: execl: ", progname ); perror( rites );
409   return EXIT_FATAL;
410 }
411
412 #endif
413
414 #endif /* BEGIN_RITES_IMPLEMENTATION: $RCSfile$: end of file */