1 #ifndef BEGIN_RITES_IMPLEMENTATION
7 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
8 * Copyright (C) 2009, 2010, MinGW Project
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.
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.
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.
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.
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.
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/gui.exe"
51 /* We wish to define a number of helper functions, which we will prefer
52 * to always compile to inline code; this convenience macro may be used
53 * to facilitate achievement of this objective.
55 #define RITES_INLINE static __inline__ __attribute__((__always_inline__))
59 * Typically, errno.h does not define a code for "no error", but
60 * it's convenient for us to have one, so that we may clear "errno"
61 * to ensure that we don't inadvertently react to a stale value.
66 #ifdef IMPLEMENT_INITIATION_RITES
68 * Normally specified by including this source file within another,
69 * with an appropriate prior definition. This indicates intent to
70 * provide an inline implementation of either first or second phase
71 * initiation rites, (mutually exclusively), depending on whether
72 * IMPLEMENT_INITIATION_RITES is defined as PHASE_ONE_RITES, or as
73 * PHASE_TWO_RITES; in either case, the associated code is invoked
74 * via the locally implemented "invoke_rites()" function.
76 # define BEGIN_RITES_IMPLEMENTATION RITES_INLINE void invoke_rites( void )
78 * In this case, since the "invoke_rites()" function has nothing
79 * to return, we declare a "do nothing" wrap-up hook.
81 # define END_RITES_IMPLEMENTATION
83 #else /* ! defined IMPLEMENT_INITIATION_RITES */
85 * This alternative case is normally achieved by free-standing
86 * compilation of rites.c; it implements the "main()" function
87 * for the "lastrites.exe" helper program.
89 # define BEGIN_RITES_IMPLEMENTATION int main()
91 * In this case, we must return an exit code; we simply assume that,
92 * for us. there is no meaningful concept of process failure, so we
93 * always report success.
95 # define END_RITES_IMPLEMENTATION return EXIT_SUCCESS;
98 /* Provide selectors, to discriminate the two distinct classes
99 * of initiation rites implementation, which may be specified as
100 * the requisite form with IMPLEMENT_INITIATION_RITES.
102 #define PHASE_ONE_RITES 1
103 #define PHASE_TWO_RITES 2
105 #if IMPLEMENT_INITIATION_RITES == PHASE_TWO_RITES
107 * For the second phase initiation rites implementation, the
108 * idea is to move the running executable and its associated DLL
109 * out of the way, so that we may install upgraded versions while
110 * the application is still running. Thus, the first step is to
111 * destroy any previously created backup copies of the running
112 * program files which may still exist...
114 # define first_act( SOURCE, BACKUP ) mingw_get_unlink( (BACKUP) )
116 * ...then, we schedule a potential pending removal of these, by
117 * initially renaming them with their designated backup names...
119 # define final_act( SOURCE, BACKUP ) mingw_get_rename( (SOURCE), (BACKUP) )
121 #else /* ! PHASE_TWO_RITES */
123 * In this case, we assume that the originally running process
124 * may have invoked phase two initiation rites, so moving its own
125 * executable and its associated DLL out of the way; we aim to move
126 * them back again, by attempting to change the backup names back
127 * to their original file names...
129 # define first_act( SOURCE, BACKUP ) mingw_get_rename( (BACKUP), (SOURCE) )
131 * We expect the preceding "rename" to succeed; if it didn't, then
132 * the most probable reason is that an upgrade has been installed,
133 * in which case we may remove the obsolete backup versions.
135 # define final_act( SOURCE, BACKUP ) mingw_get_remove( (BACKUP) )
138 RITES_INLINE const char *approot_path( void )
140 /* Inline helper to identify the root directory path for the running
141 * application, (which "mingw-get" passes through the APPROOT variable
142 * in the process environment)...
144 * Caution: although this is called more than once, DO NOT attempt to
145 * optimise getenv() lookup by saving the returned pointer across calls;
146 * the environment block may have been moved between calls, which makes
147 * the pointer returned from a previous call potentially invalid!
150 return ((approot = getenv( "APPROOT" )) == NULL)
152 ? "c:\\mingw\\" /* default, for failed environment look-up */
153 : approot; /* normal return value */
158 #if DEBUGLEVEL & DEBUG_INHIBIT_RITES_OF_PASSAGE
160 * When debugging, (with rites of passage selected for debugging),
161 * then we substitute debug message handlers for the real "rename()"
162 * and "unlink()" functions used to implement the rites.
164 RITES_INLINE int mingw_get_rename( const char *from, const char *to )
166 /* Inline debugging message handler to report requests to perform
167 * the file rename rite, with optional success/failure result.
169 fprintf( stderr, "rename: %s to %s\n", from, to );
170 errno = (DEBUGLEVEL & DEBUG_FAIL_FILE_RENAME_RITE) ? EEXIST : EOK;
171 return (DEBUGLEVEL & DEBUG_FAIL_FILE_RENAME_RITE) ? -1 : 0;
174 RITES_INLINE int mingw_get_unlink( const char *name )
176 /* Inline debugging message handler to report requests to perform
177 * the file "unlink" rite.
179 fprintf( stderr, "unlink: %s\n", name );
180 return (DEBUGLEVEL & DEBUG_FAIL_FILE_UNLINK_RITE) ? -1 : 0;
184 /* We are doing it for real...
185 * Simply map the "rename" and "unlink" requests through to the
186 * appropriate system calls.
188 # define mingw_get_rename( N1, N2 ) rename( (N1), (N2) )
189 # define mingw_get_unlink( N1 ) unlink( (N1) )
192 RITES_INLINE void mingw_get_remove( const char *name )
194 /* Inline helper to perform the "unlink" rite, provided a preceding
195 * request, (presumably "rename"), has not failed with EEXIST status.
197 if( errno == EEXIST ) mingw_get_unlink( name );
200 RITES_INLINE void perform_rites_of_passage( const wchar_t *name )
202 /* Local helper function, to perform the required rite of passage
203 * for a single specified process image file, as specified by its
204 * relative path name within the application directory tree.
206 const char *approot = approot_path();
208 /* We begin by allocating stack space for the absolute path names
209 * for both the original file name and its backup name.
211 size_t buflen = snprintf( NULL, 0, "%s%S~", approot, name );
212 char normal_name[ buflen ], backup_name[ 1 + buflen ];
214 /* Fill out this buffer pair with the requisite path names,
215 * noting that the backup name is the same as the original
216 * name, with a single tilde appended.
218 snprintf( normal_name, buflen, "%s%S", approot, name );
219 snprintf( backup_name, ++buflen, "%s~", normal_name );
221 /* Clear any pre-existing error condition, then perform the
222 * requisite "rename" and "unlink" rites, in the pre-scheduled
223 * order appropriate to the build context.
226 first_act( normal_name, backup_name );
227 final_act( normal_name, backup_name );
230 /* Here, we specify the variant portion of the implementation...
232 BEGIN_RITES_IMPLEMENTATION
234 /* ...where the requisite "rites of passage" are initiated
235 * for each process image file affected, specifying each by
236 * its path name relative to the root of the application's
237 * installation directory tree.
239 perform_rites_of_passage( MINGW_GET_EXE );
240 perform_rites_of_passage( MINGW_GET_DLL );
241 END_RITES_IMPLEMENTATION
244 #if IMPLEMENT_INITIATION_RITES == PHASE_ONE_RITES
246 * The following inline functions are required, specifically
247 * and exclusively, for the first phase of initiation rites...
249 # include <process.h>
250 # include <sys/types.h>
251 # include <sys/stat.h>
254 RITES_INLINE const char *lockfile_name( void )
256 /* Helper to identify the absolute path for the lock file...
258 static char *lockfile = NULL;
260 if( lockfile == NULL )
262 /* We resolve this only once; this is the first reference,
263 * (or all prior references were unsuccessfully resolved), so
264 * we must resolve it now.
266 const char *lockpath = approot_path();
267 const wchar_t *lockname = MINGW_GET_LCK;
268 size_t wanted = 1 + snprintf( NULL, 0, "%s%S", lockpath, lockname );
269 if( (lockfile = (char *)(malloc( wanted ))) != NULL )
270 snprintf( lockfile, wanted, "%s%S", lockpath, lockname );
272 /* In any case, we return the pointer as resolved on first call.
277 /* Provide a facility for clearing a stale lock; for Win32, we may
278 * simply refer this to the "unlink()" function, because the system
279 * will not permit us to unlink a lock file which is owned by any
280 * active process; (i.e. it is NOT a stale lock).
282 * FIXME: This will NOT work on Linux (or other Unixes), where it
283 * IS permitted to unlink an active lock file; to support
284 * such systems, we will need to provide a more robust
285 * implementation for "unlink_if_stale()".
287 #define unlink_if_stale mingw_get_unlink
291 * When guimain.h has been included, we infer that we are compiling
292 * inline components for the mingw-get GUI application; we implement
293 * custom variants of some sub-components which are GUI specific.
295 RITES_INLINE void pkgLockFail( const char *progname, const char *lockfile )
297 /* Helper function to diagnose failure to acquire the package lock.
298 * This implementation is for use in the GUI application, where there
299 * may be no stderr stream attached; construct a suitable message to
300 * be displayed in a windows message box.
302 const char *fmt = "*** ERROR *** cannot acquire catalogue lock\n%s: %s";
303 char msg[1 + snprintf( NULL, 0, fmt, lockfile, strerror( errno ) )];
304 snprintf( msg, sizeof( msg ), fmt, lockfile, strerror( errno ) );
305 MessageBox( NULL, msg, progname, MB_ICONERROR );
309 /* When guimain.h has NOT been included, and we are compiling inline,
310 * we fall back to implementing CLI specific sub-component variants.
312 RITES_INLINE void pkgLockFail( const char *progname, const char *lockfile )
314 /* Helper function to diagnose failure to acquire the package lock.
315 * This implementation is for use in the CLI application, which should
316 * always have a stderr stream attached; emit a suitable sequence of
317 * messages by simply writing to this stream.
320 "%s: cannot acquire lock for exclusive execution\n", progname
322 fprintf( stderr, "%s: ", progname ); perror( lockfile );
323 if( errno == EEXIST )
324 fprintf( stderr, "%s: another mingw-get process appears to be running\n",
330 RITES_INLINE int pkgInitRites( const char *progname )
332 /* Helper to acquire an exclusive execution lock, and if sucessful,
333 * to establish pre-conditions to permit self-upgrade.
336 const char *lockfile;
338 /* First, attempt to clear any prior (stale) lock, then create
339 * a new one. (Note that we DON'T use O_TEMPORARY here; on Win2K,
340 * it leads to strange behaviour when another process attempts to
341 * unlink a stale lock file).
343 unlink_if_stale( lockfile = lockfile_name() );
344 if( (lock = open( lockfile, O_RDWR | O_CREAT | O_EXCL, S_IWRITE )) < 0 )
346 /* We failed to acquire the lock; diagnose failure...
348 pkgLockFail( progname, lockfile );
351 /* Return the lock, indicating success or failure as appropriate.
356 RITES_INLINE int pkgLastRites( int lock, const char *progname )
358 /* Inline helper to clear the lock acquired by "pkgInitRites()",
359 * and to initiate clean-up of the changes made by "invoke_rites()"
360 * when it is invoked in second phase of initiation rites.
362 const char *approot = approot_path();
363 const char *lastrites = "libexec/mingw-get/lastrites.exe";
364 char rites[1 + snprintf( NULL, 0, "%s%s", approot, lastrites )];
366 /* Clear the lock; note that we must both close AND unlink the
367 * lock file, because we didn't open it as O_TEMPORARY.
370 unlink( lockfile_name() );
372 /* Initiate clean-up; we hand this off to a free-standing process,
373 * so that it may delete the old EXE and DLL image files belonging to
374 * this process, if they were upgraded since acquiring the lock.
376 * Note that we use the execl() function to invoke the clean-up
377 * process. However, we recognise that Microsoft's implementation
378 * of this function does NOT behave in a POSIXly correct manner;
379 * specifically, it returns control immediately to the calling
380 * process, causing it to resume execution concurrently with the
381 * exec()ed process, whereas POSIXly correct behaviour would cause
382 * the calling process to wait for the exec()ed process. This
383 * lack of POSIX-like behaviour is unfortunate, since it results
384 * in a potential race condition between the exec()ed process and
385 * the calling process, should the latter immediately attempt to
386 * invoke a new instance of mingw-get. To mitigate this potential
387 * race condition, we call the "invoke_rites()" function to pre-empt
388 * as much as possible of the processing to be performed by the
389 * clean-up program, recognising that we can be only partially
390 * successful, (but silently ignoring the partial failure), before
391 * calling execl() to complete those clean-up aspects which cannot
392 * be successfully performed in this pre-emptive fashion.
394 snprintf( rites, sizeof( rites ), "%s%s", approot, lastrites );
395 invoke_rites(); execl( rites, "lastrites", NULL );
397 /* We should never get to here; if we do...
398 * Diagnose a problem, and bail out.
400 fprintf( stderr, "%s: execl: ", progname ); perror( rites );
406 #endif /* BEGIN_RITES_IMPLEMENTATION: $RCSfile$: end of file */