1 |
/* |
2 |
* Copyright (c) 2012 Marc Alexander Lehmann <schmorp@schmorp.de> |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without modifica- |
5 |
* tion, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright notice, |
8 |
* this list of conditions and the following disclaimer. |
9 |
* |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
15 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- |
16 |
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
17 |
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- |
18 |
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
20 |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
21 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- |
22 |
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
23 |
* OF THE POSSIBILITY OF SUCH DAMAGE. |
24 |
* |
25 |
* Alternatively, the contents of this file may be used under the terms of |
26 |
* the GNU General Public License ("GPL") version 2 or any later version, |
27 |
* in which case the provisions of the GPL are applicable instead of |
28 |
* the above. If you wish to allow the use of your version of this file |
29 |
* only under the terms of the GPL and not to allow others to use your |
30 |
* version of this file under the BSD license, indicate your decision |
31 |
* by deleting the provisions above and replace them with the notice |
32 |
* and other provisions required by the GPL. If you do not delete the |
33 |
* provisions above, a recipient may use your version of this file under |
34 |
* either the BSD or the GPL. |
35 |
*/ |
36 |
|
37 |
#include "urlib.h" |
38 |
#include "urlib.c" |
39 |
|
40 |
#include <stdio.h> |
41 |
#include <stdlib.h> |
42 |
#include <unistd.h> |
43 |
#include <errno.h> |
44 |
#include <string.h> |
45 |
#include <time.h> |
46 |
|
47 |
#include "liblzf/lzf_d.c" |
48 |
|
49 |
/* some simple? dynamic memory management for paths might save ltos of ram */ |
50 |
static u_handle pack_handle; |
51 |
static u_handle lock_handle; |
52 |
static char tmppath[MAX_PATH]; |
53 |
|
54 |
static int exe_argc; |
55 |
static u_dynbuf exe_argv; |
56 |
static u_dynbuf exe_args; /* actual arguments strings copied here */ |
57 |
|
58 |
static void |
59 |
tmpdir (const char *dir) |
60 |
{ |
61 |
static int cnt; |
62 |
|
63 |
for (;;) |
64 |
{ |
65 |
// #ifdef _WIN32 |
66 |
// sprintf (tmppath, "%s/%x_%x.tmp", dir, (unsigned int)GetCurrentProcessId (), ++cnt); |
67 |
// #else |
68 |
sprintf (tmppath, "%s/t-%x_%x.tmp", dir, (unsigned int)getpid () , ++cnt); |
69 |
// #endif |
70 |
|
71 |
if (!u_mkdir (tmppath)) |
72 |
return; |
73 |
} |
74 |
} |
75 |
|
76 |
static void |
77 |
systemv (const char *const argv[]) |
78 |
{ |
79 |
#ifdef _WIN32 |
80 |
_spawnv (P_WAIT, argv [0], argv); |
81 |
#else |
82 |
pid_t pid = fork (); |
83 |
|
84 |
if (pid < 0) |
85 |
u_fatal ("fork failure"); |
86 |
|
87 |
if (!pid) |
88 |
{ |
89 |
execv (argv [0], (void *)argv); |
90 |
_exit (124); |
91 |
} |
92 |
|
93 |
int status; |
94 |
waitpid (pid, &status, 0); |
95 |
#endif |
96 |
} |
97 |
|
98 |
static void |
99 |
deltree (const char *path) |
100 |
{ |
101 |
#ifdef _WIN32 |
102 |
char buf[MAX_PATH * 2 + 64]; |
103 |
const char *argv[] = { getenv ("COMSPEC"), "/c", "rd", "/s", "/q", buf, 0 }; |
104 |
sprintf (buf, "\"%s\"", path); |
105 |
#else |
106 |
const char *argv[] = { "/bin/rm", "-rf", path, 0 }; |
107 |
#endif |
108 |
systemv (argv); |
109 |
} |
110 |
|
111 |
static char *pack_base, *pack_end; |
112 |
static struct u_pack_tail *pack_tail; |
113 |
static struct u_pack_hdr *pack_cur; |
114 |
static char *scratch; |
115 |
static unsigned int scratch_size; |
116 |
|
117 |
#define PACK_NAME ((char *)(pack_cur + 1)) |
118 |
#define PACK_DATA (PACK_NAME + u_16 (pack_cur->namelen) + 1) |
119 |
#define PACK_VALID pack_cur->type |
120 |
|
121 |
static void |
122 |
pack_next (void) |
123 |
{ |
124 |
unsigned int d = u_32 (pack_cur->datalen); |
125 |
|
126 |
pack_cur = (struct u_pack_hdr *)(PACK_DATA + d); |
127 |
} |
128 |
|
129 |
static void |
130 |
pack_unmap (void) |
131 |
{ |
132 |
if (pack_base) |
133 |
{ |
134 |
u_munmap (pack_base, pack_end - pack_base); |
135 |
pack_base = 0; |
136 |
} |
137 |
|
138 |
if (scratch) |
139 |
{ |
140 |
u_free (scratch, scratch_size); |
141 |
scratch = 0; |
142 |
} |
143 |
} |
144 |
|
145 |
static int |
146 |
pack_map (void) |
147 |
{ |
148 |
char *addr; |
149 |
unsigned int size; |
150 |
|
151 |
#ifdef _WIN32 |
152 |
BY_HANDLE_FILE_INFORMATION fi; |
153 |
|
154 |
if (!GetFileInformationByHandle (pack_handle, &fi)) |
155 |
return 0; |
156 |
|
157 |
size = fi.nFileSizeLow; |
158 |
#else |
159 |
size = lseek (pack_handle, 0, SEEK_END); |
160 |
#endif |
161 |
|
162 |
addr = u_mmap (pack_handle, size); |
163 |
if (!addr) |
164 |
return 0; |
165 |
|
166 |
/*pack_unmap ();*/ |
167 |
|
168 |
pack_base = addr; |
169 |
pack_end = pack_base + size; |
170 |
|
171 |
pack_tail = (void *)(pack_end - sizeof (*pack_tail)); |
172 |
|
173 |
if (memcmp (pack_tail->magic, TAIL_MAGIC, sizeof (TAIL_MAGIC) - 1)) |
174 |
return 0; |
175 |
|
176 |
pack_cur = (struct u_pack_hdr *)(pack_end - u_32 (pack_tail->size)); |
177 |
|
178 |
if (pack_cur->type != T_META) |
179 |
return 0; |
180 |
|
181 |
scratch = u_malloc (scratch_size = u_32 (pack_tail->max_uncompressed)); |
182 |
if (!scratch) |
183 |
return 0; |
184 |
|
185 |
return 1; |
186 |
} |
187 |
|
188 |
static void |
189 |
add_arg (char *arg, unsigned int len) |
190 |
{ |
191 |
char *addr = u_dynbuf_append (&exe_args, arg, len); |
192 |
u_dynbuf_append (&exe_argv, &addr, sizeof (addr)); |
193 |
} |
194 |
|
195 |
static void |
196 |
load (void) |
197 |
{ |
198 |
strcpy (exe_id , PACK_NAME); |
199 |
strcpy (exe_ver, PACK_DATA); |
200 |
u_set_exe_info (); |
201 |
|
202 |
if (u_chdir (exe_dir)) |
203 |
u_fatal ("unable to change to application data directory"); |
204 |
|
205 |
u_handle h = u_open ("override"); |
206 |
if (u_valid (h)) |
207 |
{ |
208 |
u_handle oh = pack_handle; |
209 |
|
210 |
pack_unmap (); |
211 |
pack_handle = h; |
212 |
|
213 |
if (pack_map () |
214 |
&& strcmp (exe_id , PACK_NAME) == 0 |
215 |
&& strcmp (exe_ver, PACK_DATA) <= 0) |
216 |
u_setenv ("URLADER_OVERRIDE", "override"); |
217 |
else |
218 |
{ |
219 |
pack_unmap (); |
220 |
pack_handle = oh; |
221 |
oh = h; |
222 |
pack_map (); |
223 |
} |
224 |
|
225 |
u_close (oh); |
226 |
} |
227 |
|
228 |
strcpy (exe_ver, PACK_DATA); |
229 |
u_set_exe_info (); |
230 |
pack_next (); |
231 |
|
232 |
for (;;) |
233 |
{ |
234 |
if (pack_cur->type == T_ENV) |
235 |
u_setenv (PACK_NAME, PACK_DATA); |
236 |
else if (pack_cur->type == T_ARG) |
237 |
add_arg (PACK_NAME, u_16 (pack_cur->namelen) + 1); |
238 |
else |
239 |
break; |
240 |
|
241 |
pack_next (); |
242 |
} |
243 |
|
244 |
done_env_arg: |
245 |
|
246 |
strcat (strcpy (tmppath, execdir), ".lck"); |
247 |
lock_handle = u_lock (tmppath, 0, 1); |
248 |
if (!lock_handle) |
249 |
u_fatal ("unable to lock application instance"); |
250 |
|
251 |
if (access (execdir, F_OK)) |
252 |
{ |
253 |
// does not exist yet, so unpack and move |
254 |
tmpdir (exe_dir); |
255 |
|
256 |
if (u_chdir (tmppath)) |
257 |
u_fatal ("unable to change to new instance directory"); |
258 |
|
259 |
for (;;) |
260 |
{ |
261 |
switch (pack_cur->type) |
262 |
{ |
263 |
case T_DIR: |
264 |
u_mkdir (PACK_NAME); |
265 |
break; |
266 |
|
267 |
case T_FILE: |
268 |
{ |
269 |
u_handle h = u_creat (PACK_NAME, pack_cur->flags & F_EXEC); |
270 |
unsigned int dlen, len = u_32 (pack_cur->datalen); |
271 |
char *data = PACK_DATA; |
272 |
|
273 |
if (pack_cur->flags & F_LZF) |
274 |
if (dlen = lzf_decompress (data, len, scratch, scratch_size)) |
275 |
{ |
276 |
data = scratch; |
277 |
len = dlen; |
278 |
} |
279 |
else |
280 |
u_fatal ("unable to uncompress file data - pack corrupted?"); |
281 |
|
282 |
if (!u_valid (h)) |
283 |
u_fatal ("unable to unpack file from packfile - disk full?"); |
284 |
|
285 |
if (u_write (h, data, len) != len) |
286 |
u_fatal ("unable to unpack file from packfile - disk full?"); |
287 |
|
288 |
u_fsync (h); |
289 |
u_close (h); |
290 |
} |
291 |
break; |
292 |
|
293 |
case T_NULL: |
294 |
goto done; |
295 |
} |
296 |
|
297 |
pack_next (); |
298 |
} |
299 |
|
300 |
done: |
301 |
if (u_chdir (datadir)) |
302 |
u_fatal ("unable to change to data directory"); |
303 |
|
304 |
u_sync (); |
305 |
|
306 |
if (u_rename (tmppath, execdir)) |
307 |
deltree (tmppath); // if move fails, delete new, assume other process created it independently |
308 |
} |
309 |
|
310 |
pack_unmap (); |
311 |
u_close (pack_handle); |
312 |
|
313 |
if (u_chdir (execdir)) |
314 |
u_fatal ("unable to change to application instance directory"); |
315 |
|
316 |
u_setenv ("URLADER_VERSION", URLADER_VERSION); |
317 |
|
318 |
#if 0 |
319 |
// yes, this is overkill |
320 |
u_setenv ("SHLIB_PATH" , execdir); // hpux |
321 |
u_setenv ("LIBPATH" , execdir); // aix |
322 |
u_setenv ("LD_LIBRARY_PATH" , execdir); // most elf systems |
323 |
u_setenv ("LD_LIBRARY_PATH_32", execdir); // solaris |
324 |
u_setenv ("LD_LIBRARY_PATH_64", execdir); // solaris |
325 |
u_setenv ("LD_LIBRARYN32_PATH", execdir); // irix |
326 |
u_setenv ("LD_LIBRARY64_PATH" , execdir); // irix |
327 |
u_setenv ("DYLD_LIBRARY_PATH" , execdir); // os sucks from apple |
328 |
#endif |
329 |
} |
330 |
|
331 |
static void |
332 |
execute (void) |
333 |
{ |
334 |
char *null = 0; |
335 |
u_dynbuf_append (&exe_argv, &null, sizeof (null)); |
336 |
systemv ((const char *const *)exe_argv.addr); |
337 |
} |
338 |
|
339 |
// this argc/argv is without argv [0] |
340 |
static void |
341 |
doit (int argc, char *argv[]) |
342 |
{ |
343 |
int i; |
344 |
|
345 |
u_setenv ("URLADER_CURRDIR", currdir); |
346 |
|
347 |
u_set_datadir (); |
348 |
u_mkdir (datadir); |
349 |
|
350 |
if (!pack_map ()) |
351 |
u_fatal ("unable to map pack file - executable corrupted?"); |
352 |
|
353 |
load (); |
354 |
|
355 |
while (argc--) |
356 |
{ |
357 |
add_arg (*argv, strlen (*argv) + 1); |
358 |
++argv; |
359 |
} |
360 |
|
361 |
execute (); |
362 |
} |
363 |
|
364 |
#ifdef _WIN32 |
365 |
|
366 |
int APIENTRY |
367 |
WinMain (HINSTANCE hI, HINSTANCE hP, LPSTR argv, int command_show) |
368 |
{ |
369 |
if (!GetModuleFileName (hI, tmppath, sizeof (tmppath))) |
370 |
u_fatal ("unable to find executable pack"); |
371 |
|
372 |
u_setenv ("URLADER_EXEPATH", tmppath); |
373 |
|
374 |
pack_handle = u_open (tmppath); |
375 |
if (!u_valid (pack_handle)) |
376 |
u_fatal ("unable to open executable pack"); |
377 |
|
378 |
if (!GetCurrentDirectory (sizeof (currdir), currdir)) |
379 |
strcpy (currdir, "."); |
380 |
|
381 |
doit (1, &argv); |
382 |
|
383 |
return 0; |
384 |
} |
385 |
|
386 |
#else |
387 |
|
388 |
int |
389 |
main (int argc, char *argv[]) |
390 |
{ |
391 |
if (!getcwd (currdir, sizeof (currdir))) |
392 |
strcpy (currdir, "."); |
393 |
|
394 |
{ |
395 |
const char *exe_path = 0; |
396 |
|
397 |
if (strchr (argv [0], '/')) |
398 |
exe_path = argv [0]; |
399 |
else |
400 |
{ |
401 |
const char *p, *path = getenv ("PATH"); |
402 |
|
403 |
if (!path) |
404 |
u_fatal ("unable to find executable, try running with full path."); |
405 |
|
406 |
for (p = path; ; ) |
407 |
{ |
408 |
const char *e = p; |
409 |
int l; |
410 |
|
411 |
while (*e && *e != ':') |
412 |
++e; |
413 |
|
414 |
l = e - p; |
415 |
memcpy (tmppath, p, l); |
416 |
|
417 |
if (!l) |
418 |
tmppath [l++] = '.'; |
419 |
|
420 |
tmppath [l++] = '/'; |
421 |
|
422 |
strcpy (tmppath + l, argv [0]); |
423 |
|
424 |
if (!access (tmppath, X_OK)) |
425 |
break; |
426 |
|
427 |
p = e; |
428 |
if (!*p) |
429 |
u_fatal ("unable to find executable, try running with full path."); |
430 |
|
431 |
++p; |
432 |
} |
433 |
|
434 |
exe_path = tmppath; |
435 |
} |
436 |
|
437 |
pack_handle = u_open (exe_path); |
438 |
if (!u_valid (pack_handle)) |
439 |
u_fatal ("unable to open executable for reading - permissions problem?"); |
440 |
|
441 |
u_setenv ("URLADER_EXEPATH", exe_path); |
442 |
} |
443 |
|
444 |
#if 0 |
445 |
/* intersperse hostname, for whatever reason */ |
446 |
|
447 |
if (gethostname (tmppath, sizeof (tmppath))) |
448 |
strcpy (tmppath, "default"); |
449 |
|
450 |
u_append (datadir, tmppath); |
451 |
#endif |
452 |
|
453 |
doit (argc - 1, argv + 1); |
454 |
|
455 |
return 0; |
456 |
} |
457 |
|
458 |
#endif |
459 |
|