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