Implement file-based post configuration for pre-inst
authorJ. Ali Harlow <ali@juiblex.co.uk>
Wed Jul 15 11:54:06 2020 +0100 (2020-07-15)
changeset 96d2d88f14283e
parent 95 212150407fcc
child 97 55ae076f393c
Implement file-based post configuration for pre-inst
pre-inst/Makefile.am
pre-inst/post.c
pre-inst/post.h
pre-inst/pre-inst.c
     1.1 --- a/pre-inst/Makefile.am	Tue Jul 14 13:17:21 2020 +0100
     1.2 +++ b/pre-inst/Makefile.am	Wed Jul 15 11:54:06 2020 +0100
     1.3 @@ -13,7 +13,7 @@
     1.4  
     1.5  bin_PROGRAMS=pre-inst
     1.6  
     1.7 -pre_inst_SOURCES=pre-inst.c
     1.8 +pre_inst_SOURCES=pre-inst.c post.c post.h
     1.9  pre_inst_LDFLAGS=$(AM_LDFLAGS) -all-static
    1.10  pre_inst_LIBTOOLFLAGS=--tag=CXX
    1.11  if HAVE_WINDRES
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/pre-inst/post.c	Wed Jul 15 11:54:06 2020 +0100
     2.3 @@ -0,0 +1,198 @@
     2.4 +/*
     2.5 + * Copyright (C) 2020  J. Ali Harlow <ali@juiblex.co.uk>
     2.6 + *
     2.7 + * This program is free software; you can redistribute it and/or modify
     2.8 + * it under the terms of the GNU General Public License as published by
     2.9 + * the Free Software Foundation; either version 2 of the License, or
    2.10 + * (at your option) any later version.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details.
    2.16 + *
    2.17 + * You should have received a copy of the GNU General Public License along
    2.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    2.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    2.20 + */
    2.21 +
    2.22 +#include <string.h>
    2.23 +#include <glib.h>
    2.24 +#include <expat.h>
    2.25 +#include <razor.h>
    2.26 +#include <plover/plover.h>
    2.27 +#include "pre-inst/post.h"
    2.28 +
    2.29 +#define PLOVER_XML_NS "http://project.juiblex.co.uk/plover/ns/2009"
    2.30 +
    2.31 +enum post_state {
    2.32 +    POST_STATE_BEGIN,
    2.33 +    POST_STATE_ROOT,
    2.34 +    POST_STATE_ARGUMENT,
    2.35 +    POST_STATE_INSTALL_PREFIX,
    2.36 +    POST_STATE_REPOSITORY,
    2.37 +};
    2.38 +
    2.39 +struct post_context {
    2.40 +    struct post *post;
    2.41 +    enum post_state state;
    2.42 +    int unknown_elements;
    2.43 +    GPtrArray *args;
    2.44 +    GString *str;
    2.45 +};
    2.46 +
    2.47 +static void post_start_element(void *data,const char *name,const char **atts)
    2.48 +{
    2.49 +    struct post_context *ctx=data;
    2.50 +    if (ctx->unknown_elements)
    2.51 +	ctx->unknown_elements++;
    2.52 +    else if (ctx->state==POST_STATE_BEGIN &&
    2.53 +      !strcmp(name,PLOVER_XML_NS "\xFF" "post"))
    2.54 +	ctx->state=POST_STATE_ROOT;
    2.55 +    else if (ctx->state==POST_STATE_ROOT &&
    2.56 +      !strcmp(name,PLOVER_XML_NS "\xFF" "command"))
    2.57 +    {
    2.58 +	ctx->state=POST_STATE_ARGUMENT;
    2.59 +	if (ctx->args->len>0)
    2.60 +	{
    2.61 +	    ctx->str=g_ptr_array_index(ctx->args,0);
    2.62 +	    g_string_truncate(ctx->str,0);
    2.63 +	}
    2.64 +	else
    2.65 +	{
    2.66 +	    ctx->str=g_string_new(NULL);
    2.67 +	    g_ptr_array_add(ctx->args,ctx->str);
    2.68 +	}
    2.69 +    }
    2.70 +    else if (ctx->state==POST_STATE_ROOT &&
    2.71 +      !strcmp(name,PLOVER_XML_NS "\xFF" "argument"))
    2.72 +    {
    2.73 +	ctx->state=POST_STATE_ARGUMENT;
    2.74 +	ctx->str=g_string_new(NULL);
    2.75 +	if (ctx->args->len==0)
    2.76 +	    g_ptr_array_add(ctx->args,NULL);
    2.77 +	g_ptr_array_add(ctx->args,ctx->str);
    2.78 +    }
    2.79 +    else if (ctx->state==POST_STATE_ARGUMENT)
    2.80 +    {
    2.81 +	if (!strcmp(name,PLOVER_XML_NS "\xFF" "install-prefix"))
    2.82 +	    ctx->state=POST_STATE_INSTALL_PREFIX;
    2.83 +	else if (!strcmp(name,PLOVER_XML_NS "\xFF" "repository"))
    2.84 +	    ctx->state=POST_STATE_REPOSITORY;
    2.85 +	else
    2.86 +	    ctx->unknown_elements++;
    2.87 +    }
    2.88 +    else
    2.89 +	ctx->unknown_elements++;
    2.90 +}
    2.91 +
    2.92 +static void post_end_element(void *data,const char *name)
    2.93 +{
    2.94 +    struct post_context *ctx=data;
    2.95 +    if (ctx->unknown_elements)
    2.96 +	ctx->unknown_elements--;
    2.97 +    else
    2.98 +	switch (ctx->state)
    2.99 +	{
   2.100 +	    case POST_STATE_ROOT:
   2.101 +		ctx->state=POST_STATE_BEGIN;
   2.102 +		break;
   2.103 +	    case POST_STATE_ARGUMENT:
   2.104 +		ctx->state=POST_STATE_ROOT;
   2.105 +		break;
   2.106 +	    case POST_STATE_INSTALL_PREFIX:
   2.107 +		if (ctx->post->install_prefix)
   2.108 +		    g_string_append(ctx->str,ctx->post->install_prefix);
   2.109 +		ctx->state=POST_STATE_ARGUMENT;
   2.110 +		break;
   2.111 +	    case POST_STATE_REPOSITORY:
   2.112 +		if (ctx->post->repository)
   2.113 +		    g_string_append(ctx->str,ctx->post->repository);
   2.114 +		ctx->state=POST_STATE_ARGUMENT;
   2.115 +		break;
   2.116 +	}
   2.117 +}
   2.118 +
   2.119 +static void post_character_data(void *data,const XML_Char *s,int len)
   2.120 +{
   2.121 +    struct post_context *ctx=data;
   2.122 +    switch (ctx->state)
   2.123 +    {
   2.124 +	case POST_STATE_ARGUMENT:
   2.125 +	    g_string_append_len(ctx->str,s,len);
   2.126 +	    break;
   2.127 +    }
   2.128 +}
   2.129 +
   2.130 +struct post *pre_install_post_new(const char *repository,
   2.131 +  const char *install_prefix)
   2.132 +{
   2.133 +    struct post *post;
   2.134 +    post=g_new0(struct post,1);
   2.135 +    post->repository=g_strdup(repository);
   2.136 +    post->install_prefix=g_strdup(install_prefix);
   2.137 +    return post;
   2.138 +}
   2.139 +
   2.140 +void pre_install_post_free(struct post *post)
   2.141 +{
   2.142 +    g_free(post->repository);
   2.143 +    g_free(post->install_prefix);
   2.144 +    g_free(post);
   2.145 +}
   2.146 +
   2.147 +gboolean pre_install_post_load_uri(struct post *post,const char *uri,
   2.148 +  GError **error)
   2.149 +{
   2.150 +    int i;
   2.151 +    gboolean retval;
   2.152 +    struct post_context ctx={0,};
   2.153 +    struct razor_error *tmp_error=NULL;
   2.154 +    size_t length;
   2.155 +    void *contents;
   2.156 +    XML_Parser parser;
   2.157 +    contents=razor_uri_get_contents(uri,&length,FALSE,&tmp_error);
   2.158 +    if (!contents)
   2.159 +    {
   2.160 +	plover_propagate_razor_error(error,tmp_error);
   2.161 +	return FALSE;
   2.162 +    }
   2.163 +    parser=XML_ParserCreateNS(NULL,'\xFF');
   2.164 +    ctx.post=post;
   2.165 +    ctx.state=POST_STATE_BEGIN;
   2.166 +    ctx.args=g_ptr_array_new();
   2.167 +    XML_SetUserData(parser,&ctx);
   2.168 +    XML_SetElementHandler(parser,post_start_element,post_end_element);
   2.169 +    XML_SetCharacterDataHandler(parser,post_character_data);
   2.170 +    retval=XML_Parse(parser,contents,length,TRUE)!=XML_STATUS_ERROR;
   2.171 +    if (!retval)
   2.172 +	g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
   2.173 +	  "%s on line %d of '%s'\n",
   2.174 +	  XML_ErrorString(XML_GetErrorCode(parser)),
   2.175 +	  XML_GetCurrentLineNumber(parser),uri);
   2.176 +    XML_ParserFree(parser);
   2.177 +    razor_uri_free_contents(contents,length);
   2.178 +    if (retval)
   2.179 +    {
   2.180 +	if (ctx.args->len>0 && !g_ptr_array_index(ctx.args,0))
   2.181 +	    g_warning("post: ignoring arguments because no command specified");
   2.182 +	if (post->argv)
   2.183 +	    g_strfreev(post->argv);
   2.184 +	if (ctx.args->len>0 && g_ptr_array_index(ctx.args,0))
   2.185 +	{
   2.186 +	    post->argc=ctx.args->len;
   2.187 +	    post->argv=g_new(gchar *,post->argc+1);
   2.188 +	    for(i=0;i<post->argc;i++)
   2.189 +		post->argv[i]=
   2.190 +		  g_string_free(g_ptr_array_index(ctx.args,i),FALSE);
   2.191 +	    post->argv[i]=NULL;
   2.192 +	}
   2.193 +	else
   2.194 +	{
   2.195 +	    post->argc=0;
   2.196 +	    post->argv=g_new0(gchar *,1);
   2.197 +	}
   2.198 +	g_ptr_array_free(ctx.args,FALSE);
   2.199 +    }
   2.200 +    return retval;
   2.201 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/pre-inst/post.h	Wed Jul 15 11:54:06 2020 +0100
     3.3 @@ -0,0 +1,24 @@
     3.4 +#ifndef __POST_H__
     3.5 +#define __POST_H__
     3.6 +
     3.7 +#include <glib.h>
     3.8 +
     3.9 +G_BEGIN_DECLS
    3.10 +
    3.11 +struct post
    3.12 +{
    3.13 +    gchar *install_prefix;
    3.14 +    gchar *repository;
    3.15 +    int argc;
    3.16 +    char **argv;
    3.17 +};
    3.18 +
    3.19 +struct post *pre_install_post_new(const char *repository,
    3.20 +  const char *install_prefix);
    3.21 +void pre_install_post_free(struct post *post);
    3.22 +gboolean pre_install_post_load_uri(struct post *post,const char *uri,
    3.23 +  GError **error);
    3.24 +
    3.25 +G_END_DECLS
    3.26 +
    3.27 +#endif /* __POST_H__ */
     4.1 --- a/pre-inst/pre-inst.c	Tue Jul 14 13:17:21 2020 +0100
     4.2 +++ b/pre-inst/pre-inst.c	Wed Jul 15 11:54:06 2020 +0100
     4.3 @@ -1,5 +1,5 @@
     4.4  /*
     4.5 - * Copyright (C) 2014  J. Ali Harlow <ali@juiblex.co.uk>
     4.6 + * Copyright (C) 2014, 2020  J. Ali Harlow <ali@juiblex.co.uk>
     4.7   *
     4.8   * This program is free software; you can redistribute it and/or modify
     4.9   * it under the terms of the GNU General Public License as published by
    4.10 @@ -44,6 +44,7 @@
    4.11  #else	/* WIN32 */
    4.12  #include <ftw.h>
    4.13  #endif	/* WIN32 */
    4.14 +#include "post.h"
    4.15  
    4.16  #ifdef WIN32
    4.17  /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */
    4.18 @@ -337,6 +338,47 @@
    4.19  
    4.20  #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
    4.21  
    4.22 +gboolean pre_install_spawn_sync(char **argv,GError **error)
    4.23 +{
    4.24 +    GError *tmp_error=NULL;
    4.25 +#ifdef USE_G_SPAWN
    4.26 +    gchar *standard_output,*standard_error;
    4.27 +    int exit_status;
    4.28 +    if (!g_spawn_sync(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
    4.29 +      &standard_output,&standard_error,&exit_status,&tmp_error))
    4.30 +    {
    4.31 +	fprintf(stderr,"Failed to start post command\n");
    4.32 +	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
    4.33 +	return FALSE;
    4.34 +    }
    4.35 +    if (standard_output && *standard_output)
    4.36 +    {
    4.37 +	printf("Output from post command %s:\n",argv[0]);
    4.38 +	fputs(standard_output,stdout);
    4.39 +    }
    4.40 +    g_free(standard_output);
    4.41 +    if (standard_error && *standard_error)
    4.42 +    {
    4.43 +	printf("Error output from post command %s:\n",argv[0]);
    4.44 +	fputs(standard_error,stdout);
    4.45 +    }
    4.46 +    g_free(standard_error);
    4.47 +    if (!g_spawn_check_exit_status(exit_status,&tmp_error))
    4.48 +    {
    4.49 +	fprintf(stderr,"post command failed\n");
    4.50 +	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
    4.51 +	return FALSE;
    4.52 +    }
    4.53 +#else
    4.54 +    if (!spawn_sync(argv,&tmp_error))
    4.55 +    {
    4.56 +	g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]);
    4.57 +	return FALSE;
    4.58 +    }
    4.59 +#endif
    4.60 +    return TRUE;
    4.61 +}
    4.62 +
    4.63  /*
    4.64   * Run a command after completing request.
    4.65   *
    4.66 @@ -354,10 +396,6 @@
    4.67      char *s;
    4.68      gchar *expanded;
    4.69      gchar **post_argv;
    4.70 -#ifdef USE_G_SPAWN
    4.71 -    gchar *standard_output,*standard_error;
    4.72 -    int exit_status;
    4.73 -#endif
    4.74      GError *tmp_error=NULL;
    4.75      if (argc<2)
    4.76      {
    4.77 @@ -401,40 +439,7 @@
    4.78  	    post_argv[i]=expanded;
    4.79  	}
    4.80      }
    4.81 -#ifdef USE_G_SPAWN
    4.82 -    if (!g_spawn_sync(NULL,post_argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
    4.83 -      &standard_output,&standard_error,&exit_status,&tmp_error))
    4.84 -    {
    4.85 -	fprintf(stderr,"Failed to start post command\n");
    4.86 -	g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
    4.87 -	return FALSE;
    4.88 -    }
    4.89 -    if (standard_output && *standard_output)
    4.90 -    {
    4.91 -	printf("Output from post command %s:\n",post_argv[0]);
    4.92 -	fputs(standard_output,stdout);
    4.93 -    }
    4.94 -    g_free(standard_output);
    4.95 -    if (standard_error && *standard_error)
    4.96 -    {
    4.97 -	printf("Error output from post command %s:\n",post_argv[0]);
    4.98 -	fputs(standard_error,stdout);
    4.99 -    }
   4.100 -    g_free(standard_error);
   4.101 -    if (!g_spawn_check_exit_status(exit_status,&tmp_error))
   4.102 -    {
   4.103 -	fprintf(stderr,"post command failed\n");
   4.104 -	g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
   4.105 -	return FALSE;
   4.106 -    }
   4.107 -#else
   4.108 -    if (!spawn_sync(post_argv,&tmp_error))
   4.109 -    {
   4.110 -	g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
   4.111 -	return FALSE;
   4.112 -    }
   4.113 -#endif
   4.114 -    return TRUE;
   4.115 +    return pre_install_spawn_sync(post_argv,error);
   4.116  }
   4.117  
   4.118  #ifdef WIN32
   4.119 @@ -498,6 +503,71 @@
   4.120      return uri;
   4.121  }
   4.122  
   4.123 +gboolean pre_install_post(int argc,char **argv,gboolean success,
   4.124 +  const char *repository)
   4.125 +{
   4.126 +    gchar *s,*uri;
   4.127 +    struct post *post=NULL;
   4.128 +    GError *error=NULL;
   4.129 +    if (!argv && !success)
   4.130 +    {
   4.131 +	/*
   4.132 +	 * If no --post command has been given, then we want to run the
   4.133 +	 * program specified in %INSTALL_PREFIX%/etc/pre-inst.post
   4.134 +	 * However, if the pre-install failed, then that file isn't likely
   4.135 +	 * to exist (and it's requirements even less so) so we issue an error
   4.136 +	 * instead. Unfortunately, it's hard to include the error message
   4.137 +	 * here. It will have been logged in the plover log, but that's not
   4.138 +	 * much help to users.
   4.139 +	 */
   4.140 +#ifndef WIN32
   4.141 +	fprintf(stderr,
   4.142 +	  "Installation failed: Failed to unpack the main installer\n");
   4.143 +#else
   4.144 +	MessageBox(NULL,"Failed to unpack the main installer",
   4.145 +	  "Installation failed",MB_ICONERROR|MB_OK);
   4.146 +#endif
   4.147 +	return FALSE;
   4.148 +    }
   4.149 +    if (!argv)
   4.150 +    {
   4.151 +	post=pre_install_post_new(repository,prefix);
   4.152 +	uri=razor_path_to_uri(prefix);
   4.153 +	s=g_strconcat(uri,"/etc/pre-inst.post",NULL);
   4.154 +	g_free(uri);
   4.155 +	pre_install_post_load_uri(post,s,&error);
   4.156 +	if (error)
   4.157 +	    g_debug("Failed to load post configuration from pre-inst.post: %s",
   4.158 +	      error->message);
   4.159 +	g_free(s);
   4.160 +	if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
   4.161 +	{
   4.162 +	    /*
   4.163 +	     * If no post configuration file exists, that's not an error;
   4.164 +	     * there simply is no post action configured.
   4.165 +	     */
   4.166 +	    g_clear_error(&error);
   4.167 +	}
   4.168 +    }
   4.169 +    if (post && post->argc>0)
   4.170 +	pre_install_spawn_sync(post->argv,&error);
   4.171 +    else if (argv)
   4.172 +	run_post(argc,argv,success,repository,&error);
   4.173 +    if (post)
   4.174 +	pre_install_post_free(post);
   4.175 +    if (error)
   4.176 +    {
   4.177 +#ifndef WIN32
   4.178 +	fprintf(stderr,"Error in post: %s\n",error->message);
   4.179 +#else
   4.180 +	MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
   4.181 +#endif
   4.182 +	g_error_free(error);
   4.183 +	success=FALSE;
   4.184 +    }
   4.185 +    return success;
   4.186 +}
   4.187 +
   4.188  int main(int argc,char **argv)
   4.189  {
   4.190      gboolean success,uninstall=FALSE,enable_post=FALSE;
   4.191 @@ -573,16 +643,10 @@
   4.192  	success=!!pre_install(repository);
   4.193  #endif
   4.194      }
   4.195 -    if (enable_post && !run_post(argc,argv,success,repository,&error))
   4.196 -    {
   4.197 -#ifndef WIN32
   4.198 -	fprintf(stderr,"Error in post: %s\n",error->message);
   4.199 -#else
   4.200 -	MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
   4.201 -#endif
   4.202 -	g_error_free(error);
   4.203 -	success=FALSE;
   4.204 -    }
   4.205 +    if (enable_post)
   4.206 +	success=pre_install_post(argc,argv,success,repository);
   4.207 +    else if (!uninstall)
   4.208 +	success=pre_install_post(0,NULL,success,repository);
   4.209  #ifdef WIN32
   4.210      return success?EXIT_SUCCESS:EXIT_FAILURE;
   4.211  #else