# HG changeset patch # User J. Ali Harlow # Date 1594810446 -3600 # Node ID d2d88f14283eae55b74200fd02b4bd1e985113c9 # Parent 212150407fcc6f1eb1d98bbe907757412336803c Implement file-based post configuration for pre-inst diff -r 212150407fcc -r d2d88f14283e pre-inst/Makefile.am --- a/pre-inst/Makefile.am Tue Jul 14 13:17:21 2020 +0100 +++ b/pre-inst/Makefile.am Wed Jul 15 11:54:06 2020 +0100 @@ -13,7 +13,7 @@ bin_PROGRAMS=pre-inst -pre_inst_SOURCES=pre-inst.c +pre_inst_SOURCES=pre-inst.c post.c post.h pre_inst_LDFLAGS=$(AM_LDFLAGS) -all-static pre_inst_LIBTOOLFLAGS=--tag=CXX if HAVE_WINDRES diff -r 212150407fcc -r d2d88f14283e pre-inst/post.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pre-inst/post.c Wed Jul 15 11:54:06 2020 +0100 @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include "pre-inst/post.h" + +#define PLOVER_XML_NS "http://project.juiblex.co.uk/plover/ns/2009" + +enum post_state { + POST_STATE_BEGIN, + POST_STATE_ROOT, + POST_STATE_ARGUMENT, + POST_STATE_INSTALL_PREFIX, + POST_STATE_REPOSITORY, +}; + +struct post_context { + struct post *post; + enum post_state state; + int unknown_elements; + GPtrArray *args; + GString *str; +}; + +static void post_start_element(void *data,const char *name,const char **atts) +{ + struct post_context *ctx=data; + if (ctx->unknown_elements) + ctx->unknown_elements++; + else if (ctx->state==POST_STATE_BEGIN && + !strcmp(name,PLOVER_XML_NS "\xFF" "post")) + ctx->state=POST_STATE_ROOT; + else if (ctx->state==POST_STATE_ROOT && + !strcmp(name,PLOVER_XML_NS "\xFF" "command")) + { + ctx->state=POST_STATE_ARGUMENT; + if (ctx->args->len>0) + { + ctx->str=g_ptr_array_index(ctx->args,0); + g_string_truncate(ctx->str,0); + } + else + { + ctx->str=g_string_new(NULL); + g_ptr_array_add(ctx->args,ctx->str); + } + } + else if (ctx->state==POST_STATE_ROOT && + !strcmp(name,PLOVER_XML_NS "\xFF" "argument")) + { + ctx->state=POST_STATE_ARGUMENT; + ctx->str=g_string_new(NULL); + if (ctx->args->len==0) + g_ptr_array_add(ctx->args,NULL); + g_ptr_array_add(ctx->args,ctx->str); + } + else if (ctx->state==POST_STATE_ARGUMENT) + { + if (!strcmp(name,PLOVER_XML_NS "\xFF" "install-prefix")) + ctx->state=POST_STATE_INSTALL_PREFIX; + else if (!strcmp(name,PLOVER_XML_NS "\xFF" "repository")) + ctx->state=POST_STATE_REPOSITORY; + else + ctx->unknown_elements++; + } + else + ctx->unknown_elements++; +} + +static void post_end_element(void *data,const char *name) +{ + struct post_context *ctx=data; + if (ctx->unknown_elements) + ctx->unknown_elements--; + else + switch (ctx->state) + { + case POST_STATE_ROOT: + ctx->state=POST_STATE_BEGIN; + break; + case POST_STATE_ARGUMENT: + ctx->state=POST_STATE_ROOT; + break; + case POST_STATE_INSTALL_PREFIX: + if (ctx->post->install_prefix) + g_string_append(ctx->str,ctx->post->install_prefix); + ctx->state=POST_STATE_ARGUMENT; + break; + case POST_STATE_REPOSITORY: + if (ctx->post->repository) + g_string_append(ctx->str,ctx->post->repository); + ctx->state=POST_STATE_ARGUMENT; + break; + } +} + +static void post_character_data(void *data,const XML_Char *s,int len) +{ + struct post_context *ctx=data; + switch (ctx->state) + { + case POST_STATE_ARGUMENT: + g_string_append_len(ctx->str,s,len); + break; + } +} + +struct post *pre_install_post_new(const char *repository, + const char *install_prefix) +{ + struct post *post; + post=g_new0(struct post,1); + post->repository=g_strdup(repository); + post->install_prefix=g_strdup(install_prefix); + return post; +} + +void pre_install_post_free(struct post *post) +{ + g_free(post->repository); + g_free(post->install_prefix); + g_free(post); +} + +gboolean pre_install_post_load_uri(struct post *post,const char *uri, + GError **error) +{ + int i; + gboolean retval; + struct post_context ctx={0,}; + struct razor_error *tmp_error=NULL; + size_t length; + void *contents; + XML_Parser parser; + contents=razor_uri_get_contents(uri,&length,FALSE,&tmp_error); + if (!contents) + { + plover_propagate_razor_error(error,tmp_error); + return FALSE; + } + parser=XML_ParserCreateNS(NULL,'\xFF'); + ctx.post=post; + ctx.state=POST_STATE_BEGIN; + ctx.args=g_ptr_array_new(); + XML_SetUserData(parser,&ctx); + XML_SetElementHandler(parser,post_start_element,post_end_element); + XML_SetCharacterDataHandler(parser,post_character_data); + retval=XML_Parse(parser,contents,length,TRUE)!=XML_STATUS_ERROR; + if (!retval) + g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "%s on line %d of '%s'\n", + XML_ErrorString(XML_GetErrorCode(parser)), + XML_GetCurrentLineNumber(parser),uri); + XML_ParserFree(parser); + razor_uri_free_contents(contents,length); + if (retval) + { + if (ctx.args->len>0 && !g_ptr_array_index(ctx.args,0)) + g_warning("post: ignoring arguments because no command specified"); + if (post->argv) + g_strfreev(post->argv); + if (ctx.args->len>0 && g_ptr_array_index(ctx.args,0)) + { + post->argc=ctx.args->len; + post->argv=g_new(gchar *,post->argc+1); + for(i=0;iargc;i++) + post->argv[i]= + g_string_free(g_ptr_array_index(ctx.args,i),FALSE); + post->argv[i]=NULL; + } + else + { + post->argc=0; + post->argv=g_new0(gchar *,1); + } + g_ptr_array_free(ctx.args,FALSE); + } + return retval; +} diff -r 212150407fcc -r d2d88f14283e pre-inst/post.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pre-inst/post.h Wed Jul 15 11:54:06 2020 +0100 @@ -0,0 +1,24 @@ +#ifndef __POST_H__ +#define __POST_H__ + +#include + +G_BEGIN_DECLS + +struct post +{ + gchar *install_prefix; + gchar *repository; + int argc; + char **argv; +}; + +struct post *pre_install_post_new(const char *repository, + const char *install_prefix); +void pre_install_post_free(struct post *post); +gboolean pre_install_post_load_uri(struct post *post,const char *uri, + GError **error); + +G_END_DECLS + +#endif /* __POST_H__ */ diff -r 212150407fcc -r d2d88f14283e pre-inst/pre-inst.c --- a/pre-inst/pre-inst.c Tue Jul 14 13:17:21 2020 +0100 +++ b/pre-inst/pre-inst.c Wed Jul 15 11:54:06 2020 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 J. Ali Harlow + * Copyright (C) 2014, 2020 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +44,7 @@ #else /* WIN32 */ #include #endif /* WIN32 */ +#include "post.h" #ifdef WIN32 /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */ @@ -337,6 +338,47 @@ #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */ +gboolean pre_install_spawn_sync(char **argv,GError **error) +{ + GError *tmp_error=NULL; +#ifdef USE_G_SPAWN + gchar *standard_output,*standard_error; + int exit_status; + if (!g_spawn_sync(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL, + &standard_output,&standard_error,&exit_status,&tmp_error)) + { + fprintf(stderr,"Failed to start post command\n"); + g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]); + return FALSE; + } + if (standard_output && *standard_output) + { + printf("Output from post command %s:\n",argv[0]); + fputs(standard_output,stdout); + } + g_free(standard_output); + if (standard_error && *standard_error) + { + printf("Error output from post command %s:\n",argv[0]); + fputs(standard_error,stdout); + } + g_free(standard_error); + if (!g_spawn_check_exit_status(exit_status,&tmp_error)) + { + fprintf(stderr,"post command failed\n"); + g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]); + return FALSE; + } +#else + if (!spawn_sync(argv,&tmp_error)) + { + g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[0]); + return FALSE; + } +#endif + return TRUE; +} + /* * Run a command after completing request. * @@ -354,10 +396,6 @@ char *s; gchar *expanded; gchar **post_argv; -#ifdef USE_G_SPAWN - gchar *standard_output,*standard_error; - int exit_status; -#endif GError *tmp_error=NULL; if (argc<2) { @@ -401,40 +439,7 @@ post_argv[i]=expanded; } } -#ifdef USE_G_SPAWN - if (!g_spawn_sync(NULL,post_argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL, - &standard_output,&standard_error,&exit_status,&tmp_error)) - { - fprintf(stderr,"Failed to start post command\n"); - g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]); - return FALSE; - } - if (standard_output && *standard_output) - { - printf("Output from post command %s:\n",post_argv[0]); - fputs(standard_output,stdout); - } - g_free(standard_output); - if (standard_error && *standard_error) - { - printf("Error output from post command %s:\n",post_argv[0]); - fputs(standard_error,stdout); - } - g_free(standard_error); - if (!g_spawn_check_exit_status(exit_status,&tmp_error)) - { - fprintf(stderr,"post command failed\n"); - g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]); - return FALSE; - } -#else - if (!spawn_sync(post_argv,&tmp_error)) - { - g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]); - return FALSE; - } -#endif - return TRUE; + return pre_install_spawn_sync(post_argv,error); } #ifdef WIN32 @@ -498,6 +503,71 @@ return uri; } +gboolean pre_install_post(int argc,char **argv,gboolean success, + const char *repository) +{ + gchar *s,*uri; + struct post *post=NULL; + GError *error=NULL; + if (!argv && !success) + { + /* + * If no --post command has been given, then we want to run the + * program specified in %INSTALL_PREFIX%/etc/pre-inst.post + * However, if the pre-install failed, then that file isn't likely + * to exist (and it's requirements even less so) so we issue an error + * instead. Unfortunately, it's hard to include the error message + * here. It will have been logged in the plover log, but that's not + * much help to users. + */ +#ifndef WIN32 + fprintf(stderr, + "Installation failed: Failed to unpack the main installer\n"); +#else + MessageBox(NULL,"Failed to unpack the main installer", + "Installation failed",MB_ICONERROR|MB_OK); +#endif + return FALSE; + } + if (!argv) + { + post=pre_install_post_new(repository,prefix); + uri=razor_path_to_uri(prefix); + s=g_strconcat(uri,"/etc/pre-inst.post",NULL); + g_free(uri); + pre_install_post_load_uri(post,s,&error); + if (error) + g_debug("Failed to load post configuration from pre-inst.post: %s", + error->message); + g_free(s); + if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT)) + { + /* + * If no post configuration file exists, that's not an error; + * there simply is no post action configured. + */ + g_clear_error(&error); + } + } + if (post && post->argc>0) + pre_install_spawn_sync(post->argv,&error); + else if (argv) + run_post(argc,argv,success,repository,&error); + if (post) + pre_install_post_free(post); + if (error) + { +#ifndef WIN32 + fprintf(stderr,"Error in post: %s\n",error->message); +#else + MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK); +#endif + g_error_free(error); + success=FALSE; + } + return success; +} + int main(int argc,char **argv) { gboolean success,uninstall=FALSE,enable_post=FALSE; @@ -573,16 +643,10 @@ success=!!pre_install(repository); #endif } - if (enable_post && !run_post(argc,argv,success,repository,&error)) - { -#ifndef WIN32 - fprintf(stderr,"Error in post: %s\n",error->message); -#else - MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK); -#endif - g_error_free(error); - success=FALSE; - } + if (enable_post) + success=pre_install_post(argc,argv,success,repository); + else if (!uninstall) + success=pre_install_post(0,NULL,success,repository); #ifdef WIN32 return success?EXIT_SUCCESS:EXIT_FAILURE; #else