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