Fix bug causing a transaction without a base to be treated as a programming error
2 * Copyright (C) 2014 J. Ali Harlow <ali@juiblex.co.uk>
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.
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.
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.
19 * http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/
24 #define _XOPEN_SOURCE 500
31 #include <plover/plover.h>
32 #include <whelk/whelk.h>
40 #define FOF_NO_UI (FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\
49 /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */
55 LUALIB_API int luaopen_posix(lua_State *L);
63 int verify_and_fix(const char *root)
69 INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param,
77 progress=GetDlgItem(dialog,IDC_PROGRESS);
78 style=GetWindowLong(progress,GWL_STYLE);
79 SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE);
80 SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30);
83 return (INT_PTR)FALSE;
90 unsigned pre_install_thread(void *data)
95 char *install[]={"plover-gtkui",NULL};
97 prefix=plover_pre_install_prefix();
98 s=g_strconcat(prefix,"/var/log/pre-install",NULL);
101 s=g_strconcat(prefix,"/var/lib/razor",NULL);
102 uri=razor_path_to_uri(s);
104 razor_set_database_uri(uri);
106 if (verify_and_fix(prefix))
112 retval=!plover_install(path,prefix,install,&error);
114 retval=!plover_update(path,prefix,NULL,&error);
117 fprintf(stderr,"%s\n",error->message);
122 PostQuitMessage(retval);
123 PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
124 _endthreadex(retval);
130 * The idea is that if pre_install() fails, update/setup should fall back
131 * to console interfaces.
138 pre_install(const char *argv0)
146 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
147 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
148 path=plover_get_program_directory(argv0);
150 retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,path,0,NULL);
152 if (pre_install_thread(path))
155 retval=(void *)1; /* Non-NULL to indicate success */
161 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
169 gboolean deltree(const char *path)
172 /* Based on g_local_file_trash() */
173 SHFILEOPSTRUCTW op={0};
177 wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
178 /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
179 wfilename=g_renew(wchar_t,wfilename,len+2);
184 success=!SHFileOperationW(&op);
185 if (success && op.fAnyOperationsAborted)
190 return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
194 gboolean pre_uninstall(void)
199 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
200 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
201 prefix=plover_pre_install_prefix();
202 s=g_strconcat(prefix,"/var/lib/razor",NULL);
203 uri=razor_path_to_uri(s);
205 razor_set_database_uri(uri);
207 success=plover_remove(NULL,&error);
210 fprintf(stderr,"%s\n",error->message);
217 #if defined(WIN32) && !defined(USE_G_SPAWN)
218 /* Based on glib's g_spawn_win32.c */
221 win32_cmdline_quote(const char *string)
223 const gchar *p=string;
226 gboolean need_dblquotes=FALSE;
229 if (*p==' ' || *p=='\t')
236 while (*pp && *pp=='\\')
244 q=retval=g_malloc(len+need_dblquotes*2+1);
255 while (*pp && *pp=='\\')
269 /* Create a win32-style wide-character argv with suitable quoting */
270 wchar_t **win32_argv_import(char **argv,GError **error)
275 GError *tmp_error=NULL;
276 n=g_strv_length(argv);
277 wargv=g_new(wchar_t *,n+1);
280 s=win32_cmdline_quote(argv[i]);
281 wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
285 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
286 "Invalid argument #%d: %s",i,tmp_error->message);
287 g_error_free(tmp_error);
298 gboolean spawn_sync(char **argv,GError **error)
300 wchar_t *wargv0,**wargv;
302 GError *tmp_error=NULL;
303 wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
306 fprintf(stderr,"Conversion error in post\n");
307 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
308 "Invalid program name: %s",tmp_error->message);
309 g_error_free(tmp_error);
312 wargv=win32_argv_import(argv,error);
315 fprintf(stderr,"Conversion error in post\n");
320 rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
322 g_strfreev((gchar **)wargv);
323 if (rc==-1 && errno!=0)
325 fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
326 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
327 "Failed to execute post command: %s",g_strerror(errno));
330 if (rc!=EXIT_SUCCESS)
332 fprintf(stderr,"post command failed (%ld)\n",(long)rc);
333 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
334 "Post command exited with code %ld",(long)rc);
340 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
343 * Run a command after completing request.
345 * Command may refer to %INSTALL_PREFIX% which will be replaced by the
346 * (first) install prefix used and/or %TEST_RESULT% which will be replaced
347 * bu either "pass" or "fail" depending as to whether the request succeeded
348 * or not. Command may also include double quotes which will be used to
349 * affect how the command is split into arguments much like a shell does.
351 gboolean run_post(int argc,char **argv,gboolean test_result,GError **error)
358 gchar *standard_output,*standard_error;
361 GError *tmp_error=NULL;
364 g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
365 "--post: No command given");
368 printf("Running post command: %s\n",argv[1]);
369 if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
371 g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
374 for(i=0;i<post_argc;i++)
376 s=strstr(post_argv[i],"%INSTALL_PREFIX%");
380 s+=strlen("%INSTALL_PREFIX%");
381 expanded=g_strconcat(post_argv[i],prefix,s,NULL);
382 g_free(post_argv[i]);
383 post_argv[i]=expanded;
385 s=strstr(post_argv[i],"%TEST_RESULT%");
389 s+=strlen("%TEST_RESULT%");
390 expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
391 g_free(post_argv[i]);
392 post_argv[i]=expanded;
396 if (!g_spawn_sync(NULL,post_argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
397 &standard_output,&standard_error,&exit_status,&tmp_error))
399 fprintf(stderr,"Failed to start post command\n");
400 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
403 if (standard_output && *standard_output)
405 printf("Output from post command %s:\n",post_argv[0]);
406 fputs(standard_output,stdout);
408 g_free(standard_output);
409 if (standard_error && *standard_error)
411 printf("Error output from post command %s:\n",post_argv[0]);
412 fputs(standard_error,stdout);
414 g_free(standard_error);
415 if (!g_spawn_check_exit_status(exit_status,&tmp_error))
417 fprintf(stderr,"post command failed\n");
418 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
422 if (!spawn_sync(post_argv,&tmp_error))
424 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
432 DWORD win32_pre_install_gui(char *argv0)
435 INITCOMMONCONTROLSEX icc={0,};
438 main_thread_id=GetCurrentThreadId();
439 thread=(HANDLE)pre_install(argv0);
442 icc.dwSize=sizeof(icc);
443 icc.dwICC=ICC_WIN95_CLASSES;
444 InitCommonControlsEx(&icc);
445 DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
446 &ProgressDialogProc);
447 while(GetMessage(&msg,NULL,0,0)>0)
449 TranslateMessage(&msg);
450 DispatchMessage(&msg);
452 WaitForSingleObject(thread,INFINITE);
453 GetExitCodeThread(thread,&retval);
459 int main(int argc,char **argv)
465 * pre-inst is normally a GUI application, but rpm scripts may well
466 * call console applications and it looks ugly if console windows keep
467 * popping up. Avoid this by allocating our own console and hiding it.
469 * - If pre-inst is a console application (typically for debugging),
470 * then skip this step.
471 * - Call ShowWindow twice to negate special handling on first call.
473 if (!GetConsoleWindow())
476 ShowWindow(GetConsoleWindow(),SW_HIDE);
477 ShowWindow(GetConsoleWindow(),SW_HIDE);
480 plover_exception_handler_init();
481 if (argc>1 && !strcmp(argv[1],"-u"))
483 success=pre_uninstall();
489 success=win32_pre_install_gui(argv[0])==EXIT_SUCCESS;
491 success=!!pre_install(argv[0]);
493 if (argc>1 && !strcmp(argv[1],"--post") &&
494 !run_post(argc-1,argv+1,success,&error))
497 fprintf(stderr,"Error in post: %s\n",error->message);
499 MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
505 return success?EXIT_SUCCESS:EXIT_FAILURE;