pre-inst/pre-inst.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Jul 15 11:54:06 2020 +0100 (2020-07-15)
changeset 96 d2d88f14283e
parent 95 212150407fcc
child 97 55ae076f393c
permissions -rw-r--r--
Implement file-based post configuration for pre-inst
     1 /*
     2  * Copyright (C) 2014, 2020  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  *
    18  * References:
    19  *	http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/
    20  */
    21 
    22 #include "config.h"
    23 #ifndef WIN32
    24 #define _XOPEN_SOURCE 500
    25 #endif
    26 #include <stdlib.h>
    27 #include <stdio.h>
    28 #include <string.h>
    29 #include <lua.h>
    30 #include <razor.h>
    31 #include <plover/plover.h>
    32 #include <whelk/whelk.h>
    33 #ifdef WIN32
    34 #include <windows.h>
    35 #include <process.h>
    36 #include <commctrl.h>
    37 #include "resource.h"
    38 
    39 #ifndef FOF_NO_UI
    40 #define FOF_NO_UI	(FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\
    41 			FOF_NOCONFIRMMKDIR)
    42 #endif
    43 
    44 #else	/* WIN32 */
    45 #include <ftw.h>
    46 #endif	/* WIN32 */
    47 #include "post.h"
    48 
    49 #ifdef WIN32
    50 /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */
    51 #undef USE_G_SPAWN
    52 #else
    53 #define USE_G_SPAWN
    54 #endif
    55 
    56 LUALIB_API int luaopen_posix(lua_State *L);
    57 
    58 #ifdef WIN32
    59 DWORD main_thread_id;
    60 #endif
    61 
    62 gchar *prefix;
    63 
    64 int verify_and_fix(const char *root)
    65 {
    66     return 0;
    67 }
    68 
    69 #ifdef WIN32
    70 INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param,
    71   LPARAM l_param)
    72 {
    73     HWND progress;
    74     DWORD style;
    75     switch (msg)
    76     {
    77 	case WM_INITDIALOG:
    78 	    progress=GetDlgItem(dialog,IDC_PROGRESS); 
    79 	    style=GetWindowLong(progress,GWL_STYLE);
    80 	    SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE);
    81 	    SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30);
    82 	    return (INT_PTR)TRUE;
    83     }
    84     return (INT_PTR)FALSE;
    85 }
    86 #endif
    87 
    88 #ifdef WIN32
    89 __stdcall
    90 #endif
    91 unsigned pre_install_thread(void *data)
    92 {
    93     int retval;
    94     const char *repository=data;
    95     gchar *s,*uri;
    96     char *install[]={"plover-gtkui",NULL};
    97     GError *error=NULL;
    98     plover__uri_handler_init();
    99     prefix=plover_pre_install_prefix();
   100     s=g_strconcat(prefix,"/var/log/pre-install",NULL);
   101     plover_log_open(s);
   102     g_free(s);
   103     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   104     uri=razor_path_to_uri(s);
   105     g_free(s);
   106     razor_set_database_uri(uri);
   107     free(uri);
   108     if (verify_and_fix(prefix))
   109     {
   110 	g_free(prefix);
   111 	return -1;
   112     }
   113     retval=!plover_install_uri(repository,prefix,install,&error);
   114     if (!retval)
   115 	retval=!plover_update_uri(repository,prefix,NULL,&error);
   116     if (error)
   117     {
   118 	fprintf(stderr,"%s\n",error->message);
   119 	g_error_free(error);
   120     }
   121 #ifdef WIN32
   122     PostQuitMessage(retval);
   123     PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
   124     _endthreadex(retval);
   125 #endif
   126     return retval;
   127 }
   128 
   129 /*
   130  * The idea is that if pre_install() fails, update/setup should fall back
   131  * to console interfaces.
   132  */
   133 #ifdef WIN32
   134 HANDLE
   135 #else
   136 void *
   137 #endif
   138 pre_install(const char *repository)
   139 {
   140 #ifdef WIN32
   141     HANDLE retval;
   142 #else
   143     void *retval;
   144 #endif
   145     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   146     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   147 #ifdef WIN32
   148     retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,(void *)repository,
   149       0,NULL);
   150 #else
   151     if (pre_install_thread((void *)repository))
   152 	retval=NULL;
   153     else
   154 	retval=(void *)1;		/* Non-NULL to indicate success */
   155 #endif
   156     return retval;
   157 }
   158 
   159 #ifndef WIN32
   160 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
   161   struct FTW *ftwbuf)
   162 {
   163     (void)remove(fpath);
   164     return 0;
   165 }
   166 #endif
   167 
   168 gboolean deltree(const char *path)
   169 {
   170 #ifdef WIN32
   171     /* Based on g_local_file_trash() */
   172     SHFILEOPSTRUCTW op={0};
   173     gboolean success;
   174     wchar_t *wfilename;
   175     long len;
   176     wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
   177     /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
   178     wfilename=g_renew(wchar_t,wfilename,len+2);
   179     wfilename[len+1]=0;
   180     op.wFunc=FO_DELETE;
   181     op.pFrom=wfilename;
   182     op.fFlags=FOF_NO_UI;
   183     success=!SHFileOperationW(&op);
   184     if (success && op.fAnyOperationsAborted)
   185 	success=FALSE;
   186     g_free(wfilename);
   187     return success;
   188 #else
   189     return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
   190 #endif
   191 }
   192 
   193 gboolean pre_uninstall(void)
   194 {
   195     gboolean success;
   196     gchar *s,*uri;
   197     GError *error=NULL;
   198     razor_set_lua_loader("posix",(void (*)())luaopen_posix);
   199     razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
   200     prefix=plover_pre_install_prefix();
   201     s=g_strconcat(prefix,"/var/lib/razor",NULL);
   202     uri=razor_path_to_uri(s);
   203     g_free(s);
   204     razor_set_database_uri(uri);
   205     free(uri);
   206     success=plover_remove(NULL,&error);
   207     if (error)
   208     {
   209 	fprintf(stderr,"%s\n",error->message);
   210 	g_error_free(error);
   211     }
   212     deltree(prefix);
   213     return success;
   214 }
   215 
   216 #if defined(WIN32) && !defined(USE_G_SPAWN)
   217 /* Based on glib's g_spawn_win32.c */
   218 
   219 static gchar *
   220 win32_cmdline_quote(const char *string)
   221 {
   222     const gchar *p=string;
   223     gchar *retval,*q;
   224     gint len=0;
   225     gboolean need_dblquotes=FALSE;
   226     while (*p)
   227     {
   228 	if (*p==' ' || *p=='\t')
   229 	    need_dblquotes=TRUE;
   230 	else if (*p=='"')
   231 	    len++;
   232 	else if (*p=='\\')
   233 	{
   234 	    const gchar *pp=p;
   235 	    while (*pp && *pp=='\\')
   236 		pp++;
   237 	    if (*pp=='"')
   238 		len++;
   239 	}
   240 	len++;
   241 	p++;
   242     }
   243     q=retval=g_malloc(len+need_dblquotes*2+1);
   244     p=string;
   245     if (need_dblquotes)
   246 	*q++='"';
   247     while (*p)
   248     {
   249 	if (*p=='"')
   250 	    *q++='\\';
   251 	else if (*p=='\\')
   252 	{
   253 	    const gchar *pp=p;
   254 	    while (*pp && *pp=='\\')
   255 		pp++;
   256 	    if (*pp=='"')
   257 		*q++='\\';
   258 	}
   259 	*q++=*p;
   260 	p++;
   261     }
   262     if (need_dblquotes)
   263 	*q++='"';
   264     *q++='\0';
   265     return retval;
   266 }
   267 
   268 /* Create a win32-style wide-character argv with suitable quoting */
   269 wchar_t **win32_argv_import(char **argv,GError **error)
   270 {
   271     int i,n;
   272     gchar *s;
   273     wchar_t **wargv;
   274     GError *tmp_error=NULL;
   275     n=g_strv_length(argv);
   276     wargv=g_new(wchar_t *,n+1);
   277     for(i=0;i<n;i++)
   278     {
   279 	s=win32_cmdline_quote(argv[i]);
   280 	wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
   281 	g_free(s);
   282 	if (!wargv[i])
   283 	{
   284 	    g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   285 	      "Invalid argument #%d: %s",i,tmp_error->message);
   286 	    g_error_free(tmp_error);
   287 	    for(i--;i>=0;i--)
   288 		g_free(wargv[i]);
   289 	    g_free(wargv);
   290 	    return FALSE;
   291 	}
   292     }
   293     wargv[i]=NULL;
   294     return wargv;
   295 }
   296 
   297 gboolean spawn_sync(char **argv,GError **error)
   298 {
   299     wchar_t *wargv0,**wargv;
   300     gintptr rc;
   301     GError *tmp_error=NULL;
   302     wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
   303     if (!wargv0)
   304     {
   305 	fprintf(stderr,"Conversion error in post\n");
   306 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   307 	  "Invalid program name: %s",tmp_error->message);
   308 	g_error_free(tmp_error);
   309 	return FALSE;
   310     }
   311     wargv=win32_argv_import(argv,error);
   312     if (!wargv)
   313     {
   314 	fprintf(stderr,"Conversion error in post\n");
   315 	g_free(wargv0);
   316 	return FALSE;
   317     }
   318     errno=0;
   319     rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
   320     g_free(wargv0);
   321     g_strfreev((gchar **)wargv);
   322     if (rc==-1 && errno!=0)
   323     {
   324 	fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
   325 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   326 	  "Failed to execute post command: %s",g_strerror(errno));
   327 	return FALSE;
   328     }
   329     if (rc!=EXIT_SUCCESS)
   330     {
   331 	fprintf(stderr,"post command failed (%ld)\n",(long)rc);
   332 	g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
   333 	  "Post command exited with code %ld",(long)rc);
   334 	return FALSE;
   335     }
   336     return TRUE;
   337 }
   338 
   339 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
   340 
   341 gboolean pre_install_spawn_sync(char **argv,GError **error)
   342 {
   343     GError *tmp_error=NULL;
   344 #ifdef USE_G_SPAWN
   345     gchar *standard_output,*standard_error;
   346     int exit_status;
   347     if (!g_spawn_sync(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
   348       &standard_output,&standard_error,&exit_status,&tmp_error))
   349     {
   350 	fprintf(stderr,"Failed to start post command\n");
   351 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   352 	return FALSE;
   353     }
   354     if (standard_output && *standard_output)
   355     {
   356 	printf("Output from post command %s:\n",argv[0]);
   357 	fputs(standard_output,stdout);
   358     }
   359     g_free(standard_output);
   360     if (standard_error && *standard_error)
   361     {
   362 	printf("Error output from post command %s:\n",argv[0]);
   363 	fputs(standard_error,stdout);
   364     }
   365     g_free(standard_error);
   366     if (!g_spawn_check_exit_status(exit_status,&tmp_error))
   367     {
   368 	fprintf(stderr,"post command failed\n");
   369 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   370 	return FALSE;
   371     }
   372 #else
   373     if (!spawn_sync(argv,&tmp_error))
   374     {
   375 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
   376 	return FALSE;
   377     }
   378 #endif
   379     return TRUE;
   380 }
   381 
   382 /*
   383  * Run a command after completing request.
   384  *
   385  * Command may refer to %INSTALL_PREFIX% which will be replaced by the
   386  * (first) install prefix used, by %TEST_RESULT% which will be replaced
   387  * by either "pass" or "fail" depending as to whether the request succeeded
   388  * or not and/or by %REPOSITORY% which will be replaced by the URI of the
   389  * repository used. Command may also include double quotes which will be used
   390  * to affect how the command is split into arguments much like a shell does.
   391  */
   392 gboolean run_post(int argc,char **argv,gboolean test_result,
   393   const char *repository,GError **error)
   394 {
   395     int i,post_argc;
   396     char *s;
   397     gchar *expanded;
   398     gchar **post_argv;
   399     GError *tmp_error=NULL;
   400     if (argc<2)
   401     {
   402 	g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
   403 	  "--post: No command given");
   404 	return FALSE;
   405     }
   406     printf("Running post command: %s\n",argv[1]);
   407     if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
   408     {
   409 	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
   410 	return FALSE;
   411     }
   412     for(i=0;i<post_argc;i++)
   413     {
   414 	s=strstr(post_argv[i],"%INSTALL_PREFIX%");
   415 	if (s)
   416 	{
   417 	    *s='\0';
   418 	    s+=strlen("%INSTALL_PREFIX%");
   419 	    expanded=g_strconcat(post_argv[i],prefix,s,NULL);
   420 	    g_free(post_argv[i]);
   421 	    post_argv[i]=expanded;
   422 	}
   423 	s=strstr(post_argv[i],"%TEST_RESULT%");
   424 	if (s)
   425 	{
   426 	    *s='\0';
   427 	    s+=strlen("%TEST_RESULT%");
   428 	    expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
   429 	    g_free(post_argv[i]);
   430 	    post_argv[i]=expanded;
   431 	}
   432 	s=strstr(post_argv[i],"%REPOSITORY%");
   433 	if (s)
   434 	{
   435 	    *s='\0';
   436 	    s+=strlen("%REPOSITORY%");
   437 	    expanded=g_strconcat(post_argv[i],repository,s,NULL);
   438 	    g_free(post_argv[i]);
   439 	    post_argv[i]=expanded;
   440 	}
   441     }
   442     return pre_install_spawn_sync(post_argv,error);
   443 }
   444 
   445 #ifdef WIN32
   446 DWORD win32_pre_install_gui(char *repository)
   447 {
   448     HANDLE thread;
   449     INITCOMMONCONTROLSEX icc={0,};
   450     MSG msg;
   451     DWORD retval;
   452     main_thread_id=GetCurrentThreadId();
   453     thread=(HANDLE)pre_install(repository);
   454     if (!thread)
   455 	return EXIT_FAILURE;
   456     icc.dwSize=sizeof(icc);
   457     icc.dwICC=ICC_WIN95_CLASSES;
   458     InitCommonControlsEx(&icc);
   459     DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
   460       &ProgressDialogProc);
   461     while(GetMessage(&msg,NULL,0,0)>0)
   462     {
   463 	TranslateMessage(&msg);
   464 	DispatchMessage(&msg);
   465     }
   466     WaitForSingleObject(thread,INFINITE);
   467     GetExitCodeThread(thread,&retval);
   468     CloseHandle(thread);
   469     return retval;
   470 }
   471 #endif	/* WIN32 */
   472 
   473 gchar *pre_install_default_repository(const char *argv0)
   474 {
   475     size_t length;
   476     void *contents;
   477     gchar *path;
   478     gchar *s,*uri;
   479     struct razor_error *tmp_error=NULL;
   480     /*
   481      * The default repository is the executable itself if it's an archive
   482      * or otherwise the directory in which the executable is stored.
   483      */
   484     path=plover_get_program(argv0);
   485     uri=razor_path_to_uri(path);
   486     free(path);
   487     s=g_strconcat(uri,"/repodata/comps.xml",NULL);
   488     contents=razor_uri_get_contents(s,&length,FALSE,&tmp_error);
   489     g_free(s);
   490     if (contents)
   491 	razor_uri_free_contents(contents,length);
   492     else
   493     {
   494 	if (razor_error_matches(tmp_error,RAZOR_GENERAL_ERROR,
   495 	  RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE))
   496 	{
   497 	    path=plover_get_program_directory(argv0);
   498 	    uri=razor_path_to_uri(path);
   499 	    free(path);
   500 	}
   501 	razor_error_free(tmp_error);
   502     }
   503     return uri;
   504 }
   505 
   506 gboolean pre_install_post(int argc,char **argv,gboolean success,
   507   const char *repository)
   508 {
   509     gchar *s,*uri;
   510     struct post *post=NULL;
   511     GError *error=NULL;
   512     if (!argv && !success)
   513     {
   514 	/*
   515 	 * If no --post command has been given, then we want to run the
   516 	 * program specified in %INSTALL_PREFIX%/etc/pre-inst.post
   517 	 * However, if the pre-install failed, then that file isn't likely
   518 	 * to exist (and it's requirements even less so) so we issue an error
   519 	 * instead. Unfortunately, it's hard to include the error message
   520 	 * here. It will have been logged in the plover log, but that's not
   521 	 * much help to users.
   522 	 */
   523 #ifndef WIN32
   524 	fprintf(stderr,
   525 	  "Installation failed: Failed to unpack the main installer\n");
   526 #else
   527 	MessageBox(NULL,"Failed to unpack the main installer",
   528 	  "Installation failed",MB_ICONERROR|MB_OK);
   529 #endif
   530 	return FALSE;
   531     }
   532     if (!argv)
   533     {
   534 	post=pre_install_post_new(repository,prefix);
   535 	uri=razor_path_to_uri(prefix);
   536 	s=g_strconcat(uri,"/etc/pre-inst.post",NULL);
   537 	g_free(uri);
   538 	pre_install_post_load_uri(post,s,&error);
   539 	if (error)
   540 	    g_debug("Failed to load post configuration from pre-inst.post: %s",
   541 	      error->message);
   542 	g_free(s);
   543 	if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
   544 	{
   545 	    /*
   546 	     * If no post configuration file exists, that's not an error;
   547 	     * there simply is no post action configured.
   548 	     */
   549 	    g_clear_error(&error);
   550 	}
   551     }
   552     if (post && post->argc>0)
   553 	pre_install_spawn_sync(post->argv,&error);
   554     else if (argv)
   555 	run_post(argc,argv,success,repository,&error);
   556     if (post)
   557 	pre_install_post_free(post);
   558     if (error)
   559     {
   560 #ifndef WIN32
   561 	fprintf(stderr,"Error in post: %s\n",error->message);
   562 #else
   563 	MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
   564 #endif
   565 	g_error_free(error);
   566 	success=FALSE;
   567     }
   568     return success;
   569 }
   570 
   571 int main(int argc,char **argv)
   572 {
   573     gboolean success,uninstall=FALSE,enable_post=FALSE;
   574     GError *error=NULL;
   575     gchar *path=NULL,*repository=NULL;
   576     GOptionContext *context;
   577     GOptionEntry options[]={
   578 	{"repository",0,0,G_OPTION_ARG_STRING,&repository,
   579 	  "Repository location","uri"},
   580 	{"path",0,0,G_OPTION_ARG_FILENAME,&path,
   581 	  "Repository path","path"},
   582 	{"uninstall",'u',0,G_OPTION_ARG_NONE,&uninstall,
   583 	  "Uninstall all packages",NULL},
   584 	{"post",0,0,G_OPTION_ARG_NONE,&enable_post,
   585 	  "Run command after request is processed",NULL},
   586 	{NULL}
   587     };
   588 #ifdef WIN32
   589     /*
   590      * pre-inst is normally a GUI application, but rpm scripts may well
   591      * call console applications and it looks ugly if console windows keep
   592      * popping up. Avoid this by allocating our own console and hiding it.
   593      * Note:
   594      *	- If pre-inst is a console application (typically for debugging),
   595      *    then skip this step.
   596      *  - Call ShowWindow twice to negate special handling on first call.
   597      */
   598     if (!GetConsoleWindow())
   599     {
   600 	AllocConsole();
   601 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   602 	ShowWindow(GetConsoleWindow(),SW_HIDE);
   603     }
   604 #endif
   605     plover_exception_handler_init();
   606     context=g_option_context_new("[command] - install the main installer");
   607     g_option_context_add_main_entries(context,options,NULL);
   608     g_option_context_set_description(context,
   609       "If --post is specified, then the command to run and its arguments\n"
   610       "should be listed at the end of the command line.\n"
   611       "Command may refer to %INSTALL_PREFIX% which will be replaced by the\n"
   612       "(first) install prefix used, by %TEST_RESULT% which will be\n"
   613       "replaced by either \"pass\" or \"fail\" depending as to whether the\n"
   614       "request succeeded or not and/or by %REPOSITORY% which will be\n"
   615       "replaced by the URI of the repository used. Command may also include\n"
   616       "double quotes which will be used to affect how the command is split\n"
   617       "into arguments much like a shell does.");
   618     g_option_context_set_strict_posix(context,TRUE);
   619     g_option_context_set_ignore_unknown_options(context,TRUE);
   620     if (!g_option_context_parse(context,&argc,&argv,&error))
   621     {
   622 	g_printerr("pre-install: %s\n",error->message);
   623 	g_printerr("Use \"%s --help\" for help\n",(*argv)[0]);
   624 	exit(1);
   625     }
   626     if (repository && path)
   627     {
   628 	g_printerr("pre-install: "
   629 	  "Only one of --repository and --path can be specified\n");
   630 	exit(1);
   631     }
   632     if (path)
   633 	repository=razor_path_to_uri(path);
   634     else if (!repository)
   635 	repository=pre_install_default_repository(argv[0]);
   636     if (uninstall)
   637 	success=pre_uninstall();
   638     else
   639     {
   640 #ifdef WIN32
   641 	success=win32_pre_install_gui(repository)==EXIT_SUCCESS;
   642 #else
   643 	success=!!pre_install(repository);
   644 #endif
   645     }
   646     if (enable_post)
   647 	success=pre_install_post(argc,argv,success,repository);
   648     else if (!uninstall)
   649 	success=pre_install_post(0,NULL,success,repository);
   650 #ifdef WIN32
   651     return success?EXIT_SUCCESS:EXIT_FAILURE;
   652 #else
   653     return success?0:1;
   654 #endif
   655 }