/* * Copyright (c) 2012 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "urlib.h" #include "urlib.c" #include #include #include #include #include #include #include "liblzf/lzf_d.c" /* some simple? dynamic memory management for paths might save ltos of ram */ static u_handle pack_handle; static u_handle lock_handle; static char tmppath[MAX_PATH]; static int exe_argc; static u_dynbuf exe_argv; static u_dynbuf exe_args; /* actual arguments strings copied here */ static void tmpdir (const char *dir) { static int cnt; for (;;) { // #ifdef _WIN32 // sprintf (tmppath, "%s/%x_%x.tmp", dir, (unsigned int)GetCurrentProcessId (), ++cnt); // #else sprintf (tmppath, "%s/t-%x_%x.tmp", dir, (unsigned int)getpid () , ++cnt); // #endif if (!u_mkdir (tmppath)) return; } } static void systemv (const char *const argv[]) { #ifdef _WIN32 _spawnv (P_WAIT, argv [0], argv); #else pid_t pid = fork (); if (pid < 0) u_fatal ("fork failure"); if (!pid) { execv (argv [0], (void *)argv); _exit (124); } int status; waitpid (pid, &status, 0); #endif } static void deltree (const char *path) { #ifdef _WIN32 char buf[MAX_PATH * 2 + 64]; const char *argv[] = { getenv ("COMSPEC"), "/c", "rd", "/s", "/q", buf, 0 }; sprintf (buf, "\"%s\"", path); #else const char *argv[] = { "/bin/rm", "-rf", path, 0 }; #endif systemv (argv); } static char *pack_base, *pack_end; static struct u_pack_tail *pack_tail; static struct u_pack_hdr *pack_cur; static char *scratch; static unsigned int scratch_size; #define PACK_NAME ((char *)(pack_cur + 1)) #define PACK_DATA (PACK_NAME + u_16 (pack_cur->namelen) + 1) #define PACK_VALID pack_cur->type static void pack_next (void) { unsigned int d = u_32 (pack_cur->datalen); pack_cur = (struct u_pack_hdr *)(PACK_DATA + d); } static void pack_unmap (void) { if (pack_base) { u_munmap (pack_base, pack_end - pack_base); pack_base = 0; } if (scratch) { u_free (scratch, scratch_size); scratch = 0; } } static int pack_map (void) { char *addr; unsigned int size; #ifdef _WIN32 BY_HANDLE_FILE_INFORMATION fi; if (!GetFileInformationByHandle (pack_handle, &fi)) return 0; size = fi.nFileSizeLow; #else size = lseek (pack_handle, 0, SEEK_END); #endif addr = u_mmap (pack_handle, size); if (!addr) return 0; /*pack_unmap ();*/ pack_base = addr; pack_end = pack_base + size; pack_tail = (void *)(pack_end - sizeof (*pack_tail)); if (memcmp (pack_tail->magic, TAIL_MAGIC, sizeof (TAIL_MAGIC) - 1)) return 0; pack_cur = (struct u_pack_hdr *)(pack_end - u_32 (pack_tail->size)); if (pack_cur->type != T_META) return 0; scratch = u_malloc (scratch_size = u_32 (pack_tail->max_uncompressed)); if (!scratch) return 0; return 1; } static void add_arg (char *arg, unsigned int len) { char *addr = u_dynbuf_append (&exe_args, arg, len); u_dynbuf_append (&exe_argv, &addr, sizeof (addr)); } static void load (void) { strcpy (exe_id , PACK_NAME); strcpy (exe_ver, PACK_DATA); u_set_exe_info (); if (u_chdir (exe_dir)) u_fatal ("unable to change to application data directory"); u_handle h = u_open ("override"); if (u_valid (h)) { u_handle oh = pack_handle; pack_unmap (); pack_handle = h; if (pack_map () && strcmp (exe_id , PACK_NAME) == 0 && strcmp (exe_ver, PACK_DATA) <= 0) u_setenv ("URLADER_OVERRIDE", "override"); else { pack_unmap (); pack_handle = oh; oh = h; pack_map (); } u_close (oh); } strcpy (exe_ver, PACK_DATA); u_set_exe_info (); pack_next (); for (;;) { if (pack_cur->type == T_ENV) u_setenv (PACK_NAME, PACK_DATA); else if (pack_cur->type == T_ARG) add_arg (PACK_NAME, u_16 (pack_cur->namelen) + 1); else break; pack_next (); } done_env_arg: strcat (strcpy (tmppath, execdir), ".lck"); lock_handle = u_lock (tmppath, 0, 1); if (!lock_handle) u_fatal ("unable to lock application instance"); if (access (execdir, F_OK)) { // does not exist yet, so unpack and move tmpdir (exe_dir); if (u_chdir (tmppath)) u_fatal ("unable to change to new instance directory"); for (;;) { switch (pack_cur->type) { case T_DIR: u_mkdir (PACK_NAME); break; case T_FILE: { u_handle h = u_creat (PACK_NAME, pack_cur->flags & F_EXEC); unsigned int dlen, len = u_32 (pack_cur->datalen); char *data = PACK_DATA; if (pack_cur->flags & F_LZF) if (dlen = lzf_decompress (data, len, scratch, scratch_size)) { data = scratch; len = dlen; } else u_fatal ("unable to uncompress file data - pack corrupted?"); if (!u_valid (h)) u_fatal ("unable to unpack file from packfile - disk full?"); if (u_write (h, data, len) != len) u_fatal ("unable to unpack file from packfile - disk full?"); u_fsync (h); u_close (h); } break; case T_NULL: goto done; } pack_next (); } done: if (u_chdir (datadir)) u_fatal ("unable to change to data directory"); u_sync (); if (u_rename (tmppath, execdir)) deltree (tmppath); // if move fails, delete new, assume other process created it independently } pack_unmap (); u_close (pack_handle); if (u_chdir (execdir)) u_fatal ("unable to change to application instance directory"); u_setenv ("URLADER_VERSION", URLADER_VERSION); #if 0 // yes, this is overkill u_setenv ("SHLIB_PATH" , execdir); // hpux u_setenv ("LIBPATH" , execdir); // aix u_setenv ("LD_LIBRARY_PATH" , execdir); // most elf systems u_setenv ("LD_LIBRARY_PATH_32", execdir); // solaris u_setenv ("LD_LIBRARY_PATH_64", execdir); // solaris u_setenv ("LD_LIBRARYN32_PATH", execdir); // irix u_setenv ("LD_LIBRARY64_PATH" , execdir); // irix u_setenv ("DYLD_LIBRARY_PATH" , execdir); // os sucks from apple #endif } static void execute (void) { char *null = 0; u_dynbuf_append (&exe_argv, &null, sizeof (null)); systemv ((const char *const *)exe_argv.addr); } // this argc/argv is without argv [0] static void doit (int argc, char *argv[]) { int i; u_setenv ("URLADER_CURRDIR", currdir); u_set_datadir (); u_mkdir (datadir); if (!pack_map ()) u_fatal ("unable to map pack file - executable corrupted?"); load (); while (argc--) { add_arg (*argv, strlen (*argv) + 1); ++argv; } execute (); } #ifdef _WIN32 int APIENTRY WinMain (HINSTANCE hI, HINSTANCE hP, LPSTR argv, int command_show) { if (!GetModuleFileName (hI, tmppath, sizeof (tmppath))) u_fatal ("unable to find executable pack"); u_setenv ("URLADER_EXEPATH", tmppath); pack_handle = u_open (tmppath); if (!u_valid (pack_handle)) u_fatal ("unable to open executable pack"); if (!GetCurrentDirectory (sizeof (currdir), currdir)) strcpy (currdir, "."); doit (1, &argv); return 0; } #else int main (int argc, char *argv[]) { if (!getcwd (currdir, sizeof (currdir))) strcpy (currdir, "."); { const char *exe_path = 0; if (strchr (argv [0], '/')) exe_path = argv [0]; else { const char *p, *path = getenv ("PATH"); if (!path) u_fatal ("unable to find executable, try running with full path."); for (p = path; ; ) { const char *e = p; int l; while (*e && *e != ':') ++e; l = e - p; memcpy (tmppath, p, l); if (!l) tmppath [l++] = '.'; tmppath [l++] = '/'; strcpy (tmppath + l, argv [0]); if (!access (tmppath, X_OK)) break; p = e; if (!*p) u_fatal ("unable to find executable, try running with full path."); ++p; } exe_path = tmppath; } pack_handle = u_open (exe_path); if (!u_valid (pack_handle)) u_fatal ("unable to open executable for reading - permissions problem?"); u_setenv ("URLADER_EXEPATH", exe_path); } #if 0 /* intersperse hostname, for whatever reason */ if (gethostname (tmppath, sizeof (tmppath))) strcpy (tmppath, "default"); u_append (datadir, tmppath); #endif doit (argc - 1, argv + 1); return 0; } #endif