ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Urlader/urlader.c
Revision: 1.7
Committed: Fri Dec 30 14:51:45 2011 UTC (12 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.6: +74 -39 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #ifndef URLADER
2     # define URLADER "urlader"
3     #endif
4 root 1.5 #define URLADER_VERSION "1.0" // a decimal number, not a version string
5 root 1.1
6 root 1.6 #define MAX_ARGC 32
7     #define MAX_ARGS 256
8    
9 root 1.1 #include <stdio.h>
10     #include <stdlib.h>
11     #include <unistd.h>
12     #include <errno.h>
13     #include <string.h>
14     #include <time.h>
15     #include <fcntl.h>
16    
17 root 1.2 #include "liblzf/lzf_d.c"
18 root 1.1
19     #define TAIL_MAGIC "SCHMORPPACK0"
20    
21     #ifdef _WIN32
22    
23 root 1.6 #include <windows.h>
24     //#include <winbase.h>
25     #include <shlobj.h>
26     #include <shlwapi.h>
27     #include <wininet.h>
28    
29     static DWORD dword;
30    
31     #define u_handle HANDLE
32     #define u_invalid_handle 0
33     #define u_valid(handle) (!!handle)
34    
35     #define u_setenv(name,value) SetEnvironmentVariable (name, value)
36     #define u_mkdir(path) !CreateDirectory (path, NULL)
37     #define u_chdir(path) !SetCurrentDirectory (path)
38     #define u_rename(fr,to) !MoveFile (fr, to)
39     #define u_open(path) CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL)
40     #define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL)
41     #define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL)
42     #define u_close(handle) CloseHandle (handle)
43     #define u_append(path,add) PathAppend (path, add)
44     #define u_write(handle,data,len) (WriteFile (handle, data, len, &dword, 0) ? dword : -1)
45    
46     #define u_fsync(handle) FlushFileBuffers (handle)
47     #define u_sync()
48    
49     #define u_lockfile(path) CreateFile (path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)
50     #define u_cloexec(handle)
51    
52     #else
53    
54     #define _GNU_SOURCE 1
55     #define _BSD_SOURCE 1
56     // the above increases our chances of getting MAP_ANONYMOUS
57    
58     #include <sys/mman.h>
59     #include <sys/types.h>
60 root 1.7 #include <sys/stat.h>
61     #include <unistd.h>
62 root 1.6 #include <pwd.h>
63 root 1.4
64 root 1.6 #if defined (MAP_ANON) && !defined (MAP_ANONYMOUS)
65     #define MAP_ANONYMOUS MAP_ANON
66     #endif
67 root 1.1
68 root 1.6 #ifdef PATH_MAX
69     #define MAX_PATH (PATH_MAX < 4096 ? 4096 : PATH_MAX)
70     #else
71     #define MAX_PATH 4096
72     #endif
73 root 1.1
74 root 1.6 #define u_handle int
75     #define u_invalid_handle -1
76     #define u_valid(fd) ((fd) >= 0)
77    
78     #define u_setenv(name,value) setenv (name, value, 1)
79     #define u_mkdir(path) mkdir (path, 0777)
80     #define u_chdir(path) chdir (path)
81     #define u_rename(fr,to) rename (fr, to)
82     #define u_open(path) open (path, O_RDONLY)
83     #define u_creat(path,exec) open (path, O_WRONLY | O_CREAT | O_TRUNC, (exec) ? 0777 : 0666)
84     #define u_close(handle) close (handle)
85     #define u_append(path,add) strcat (strcat (path, "/"), add)
86     #define u_write(handle,data,len) write (handle, data, len)
87    
88     // on a mostly idle system, a sync at the end is certainly faster, hope for the best
89     #define u_fsync(handle)
90     #define u_sync() sync ()
91    
92     #define u_lockfile(path) open (path, O_RDWR | O_CREAT, 0666)
93     #define u_cloexec(handle) fcntl (handle, F_SETFD, FD_CLOEXEC)
94 root 1.1
95     #endif
96    
97     #define u_16(ptr) (((ptr)[0] << 8) | (ptr)[1])
98     #define u_32(ptr) (((ptr)[0] << 24) | ((ptr)[1] << 16) | ((ptr)[2] << 8) | (ptr)[3])
99    
100 root 1.6 /* some simple? dynamic memory management for paths might save ltos of ram */
101 root 1.1 static u_handle pack_handle;
102 root 1.6 static u_handle lock_handle;
103 root 1.1 static char tmppath[MAX_PATH];
104     static char currdir[MAX_PATH];
105     static char datadir[MAX_PATH]; // %AppData%/urlader
106     static char exe_dir[MAX_PATH]; // %AppData%/urlader/EXE_ID
107     static char execdir[MAX_PATH]; // %AppData%/urlader/EXE_ID/EXE_VER
108     static char exe_id[MAX_PATH];
109     static char exe_ver[MAX_PATH];
110    
111     static int exe_argc;
112 root 1.6 static const char *exe_argv[MAX_ARGC];
113     static char exe_args[MAX_ARGS]; /* actual arguments strings copied here */
114     static unsigned int exe_argo;
115 root 1.1
116     static void
117     fatal (const char *msg)
118     {
119     #ifdef _WIN32
120     MessageBox (0, msg, URLADER, 0);
121     #else
122 root 1.6 write (2, URLADER ": ", sizeof (URLADER ": ") - 1);
123     write (2, msg, strlen (msg));
124     write (2, "\n", 1);
125 root 1.1 #endif
126    
127     _exit (1);
128     }
129    
130 root 1.6 static void *
131     u_malloc (unsigned int size)
132     {
133     void *addr;
134    
135     #ifdef _WIN32
136     HANDLE handle = CreateFileMapping (0, 0, PAGE_READWRITE, 0, size, NULL);
137    
138     if (!handle)
139     return 0;
140    
141     addr = MapViewOfFile (handle, FILE_MAP_WRITE, 0, 0, size);
142    
143     CloseHandle (handle);
144     #elif defined (MAP_ANONYMOUS)
145     addr = mmap (0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
146    
147     if (addr == (void *)-1)
148     addr = 0;
149     #else
150     addr = malloc (size);
151     #endif
152    
153     return addr;
154     }
155    
156     static void
157     u_free (void *addr, unsigned int size)
158     {
159     #ifdef _WIN32
160     UnmapViewOfFile (addr);
161     #elif defined (MAP_ANONYMOUS)
162     munmap (addr, size);
163     #else
164     free (addr);
165     #endif
166     }
167    
168     static void *
169     u_mmap (u_handle h, unsigned int size)
170     {
171     void *addr;
172    
173     #ifdef _WIN32
174     HANDLE handle = CreateFileMapping (h, 0, PAGE_READONLY, 0, size, NULL);
175    
176     if (!handle)
177     return 0;
178    
179     addr = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, size);
180    
181     CloseHandle (handle);
182     #else
183     addr = mmap (0, size, PROT_READ, MAP_SHARED, h, 0);
184    
185     if (addr == (void *)-1)
186     addr = 0;
187     #endif
188    
189     return addr;
190     }
191    
192     static void
193     u_munmap (void *addr, unsigned int len)
194     {
195     #ifdef _WIN32
196     UnmapViewOfFile (addr);
197     #else
198     munmap (addr, len);
199     #endif
200     }
201    
202 root 1.1 static void
203     tmpdir (const char *dir)
204     {
205     static int cnt;
206    
207     for (;;)
208     {
209     // #ifdef _WIN32
210     // sprintf (tmppath, "%s/%x_%x.tmp", dir, (unsigned int)GetCurrentProcessId (), ++cnt);
211     // #else
212     sprintf (tmppath, "%s/t-%x_%x.tmp", dir, (unsigned int)getpid () , ++cnt);
213     // #endif
214    
215     if (!u_mkdir (tmppath))
216     return;
217     }
218     }
219    
220 root 1.7 static u_handle
221     u_lock (const char *path, int excl, int dowait)
222 root 1.6 {
223 root 1.7 u_handle h;
224    
225     h = u_lockfile (path);
226     if (!u_valid (h))
227     return h;
228    
229     u_cloexec (h);
230    
231     for (;;)
232     {
233     int success;
234 root 1.6
235 root 1.7 // acquire the lock
236 root 1.6 #ifdef _WIN32
237     OVERLAPPED ov = { 0 };
238    
239 root 1.7 success = LockFileEx (h,
240     (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0)
241     | (dowait ? 0 : LOCKFILE_FAIL_IMMEDIATELY),
242     0,
243     1, 0,
244     &ov);
245     #else
246     struct flock lck = { 0 };
247    
248     lck.l_type = excl ? F_WRLCK : F_RDLCK;
249     lck.l_whence = SEEK_SET;
250     lck.l_len = 1;
251    
252     success = !fcntl (h, dowait ? F_SETLKW : F_SETLK, &lck);
253     #endif
254    
255     if (!success)
256     break;
257    
258     // we have the lock, now verify that the lockfile still exists
259    
260     #ifdef _WIN32
261     // apparently, we have to open the file to get its info :(
262     BY_HANDLE_FILE_INFORMATION s1, s2;
263     u_handle h2 = u_lockfile (path);
264     if (!u_valid (h))
265     break;
266    
267     success = GetFileInformationByHandle (h, &s1)
268     && GetFileInformationByHandle (h2, &s2);
269    
270     u_close (h2);
271    
272     if (!success)
273     break;
274    
275     success = s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber
276     && s1.nFileIndexHigh == s2.nFileIndexHigh
277     && s1.nFileIndexLow == s2.nFileIndexLow;
278 root 1.6 #else
279 root 1.7 struct stat s1, s2;
280 root 1.6
281 root 1.7 if (fstat (h, &s1) || stat (path, &s2))
282     break;
283    
284     success = s1.st_dev == s2.st_dev
285     && s1.st_ino == s2.st_ino;
286 root 1.6 #endif
287 root 1.7
288     if (success)
289     return h; // lock successfully acquired
290    
291     // files differ, close and retry - should be very rare
292     u_close (h);
293     }
294    
295     // failure
296     u_close (h);
297     return u_invalid_handle;
298 root 1.6 }
299    
300 root 1.1 static void
301 root 1.6 systemv (const char *const argv[])
302 root 1.1 {
303     #ifdef _WIN32
304     _spawnv (P_WAIT, argv [0], argv);
305     #else
306 root 1.6 pid_t pid = fork ();
307 root 1.1
308     if (pid < 0)
309     fatal ("fork failure");
310    
311     if (!pid)
312     {
313     execv (argv [0], (void *)argv);
314     _exit (124);
315     }
316    
317     int status;
318     waitpid (pid, &status, 0);
319     #endif
320     }
321    
322     static void
323     deltree (const char *path)
324     {
325     #ifdef _WIN32
326     char buf[MAX_PATH * 2 + 64];
327     const char *argv[] = { getenv ("COMSPEC"), "/c", "rd", "/s", "/q", buf, 0 };
328     sprintf (buf, "\"%s\"", path);
329     #else
330     const char *argv[] = { "/bin/rm", "-rf", path, 0 };
331     #endif
332 root 1.6 systemv (argv);
333 root 1.1 }
334    
335     enum
336     {
337     T_NULL, // 5
338     T_META, // 1 : exe_id, exe_ver
339 root 1.4 T_ENV, // 2 : name, value
340     T_ARG, // 3 : arg
341 root 1.1 T_DIR, // 4+: path
342     T_FILE, // 4+: path, data
343     T_NUM
344     };
345    
346     enum
347     {
348     F_EXEC = 1,
349     F_LZF = 2,
350     F_NULL = 0
351     };
352    
353     struct hdr
354     {
355     unsigned char type;
356     unsigned char flags;
357     unsigned char namelen[2];
358     unsigned char datalen[4];
359     };
360    
361     struct tail {
362     unsigned char max_filesize[4];
363     unsigned char size[4];
364     char magic[12];
365     };
366    
367     static char *pack_base, *pack_end;
368     static struct hdr *pack_cur;
369 root 1.2 static char *scratch;
370 root 1.6 static unsigned int scratch_size;
371 root 1.1
372     #define PACK_NAME ((char *)(pack_cur + 1))
373     #define PACK_DATA (PACK_NAME + u_16 (pack_cur->namelen) + 1)
374     #define PACK_VALID pack_cur->type
375    
376     static void
377     pack_next (void)
378     {
379     unsigned int d = u_32 (pack_cur->datalen);
380    
381     pack_cur = (struct hdr *)(PACK_DATA + d);
382     }
383    
384     static void
385     pack_unmap (void)
386     {
387 root 1.6 if (pack_base)
388     {
389     u_munmap (pack_base, pack_end - pack_base);
390     pack_base = 0;
391     }
392 root 1.1
393 root 1.6 if (scratch)
394     {
395     u_free (scratch, scratch_size);
396     scratch = 0;
397     }
398 root 1.1 }
399    
400     static int
401     pack_map (void)
402     {
403 root 1.6 char *addr;
404     unsigned int size;
405    
406 root 1.1 #ifdef _WIN32
407     BY_HANDLE_FILE_INFORMATION fi;
408    
409     if (!GetFileInformationByHandle (pack_handle, &fi))
410     return 0;
411    
412 root 1.6 size = fi.nFileSizeLow;
413     #else
414     size = lseek (pack_handle, 0, SEEK_END);
415     #endif
416 root 1.1
417 root 1.6 addr = u_mmap (pack_handle, size);
418     if (!addr)
419 root 1.1 return 0;
420    
421     pack_unmap ();
422    
423 root 1.6 pack_base = addr;
424     pack_end = pack_base + size;
425 root 1.1
426     struct tail *tail;
427    
428     tail = (void *)(pack_end - sizeof (*tail));
429    
430     if (memcmp (tail->magic, TAIL_MAGIC, sizeof (TAIL_MAGIC) - 1))
431     return 0;
432    
433     pack_cur = (struct hdr *)(pack_end - u_32 (tail->size));
434    
435 root 1.6 scratch = u_malloc (scratch_size = u_32 (tail->max_filesize));
436     if (!scratch)
437     return 0;
438 root 1.2
439 root 1.1 return 1;
440     }
441    
442     static void
443     exe_info (void)
444     {
445     if (!pack_map ())
446     fatal ("unable to locate packfile in executable - executable corrupted?");
447    
448     if (pack_cur->type != T_META)
449     fatal ("unable to locate executable metadata - executable corrupted?");
450    
451     strcpy (exe_id, PACK_NAME);
452     }
453    
454     static void
455     load (void)
456     {
457     u_mkdir (datadir);
458    
459     strcpy (exe_dir, datadir);
460     u_append (exe_dir, exe_id);
461     u_mkdir (exe_dir);
462    
463     if (u_chdir (exe_dir))
464     fatal ("unable to change to application data directory");
465    
466     u_handle h = u_open ("override");
467 root 1.5 if (u_valid (h))
468 root 1.1 {
469     u_handle oh = pack_handle;
470    
471     pack_handle = h;
472     if (!pack_map ())
473     {
474     pack_handle = oh;
475     oh = h;
476     }
477    
478     u_close (oh);
479     }
480    
481     if (pack_cur->type != T_META)
482     fatal ("unable to locate override metadata");
483    
484     strcpy (exe_ver, PACK_DATA);
485     pack_next ();
486    
487 root 1.4 for (;;)
488 root 1.1 {
489 root 1.4 if (pack_cur->type == T_ENV)
490     u_setenv (PACK_NAME, PACK_DATA);
491     else if (pack_cur->type == T_ARG)
492 root 1.6 {
493     int len = u_16 (pack_cur->namelen) + 1;
494     exe_argv [exe_argc++] = exe_args + exe_argo;
495     memcpy (exe_args + exe_argo, PACK_NAME, len);
496     exe_argo += len;
497     }
498 root 1.4 else
499     break;
500 root 1.1
501     pack_next ();
502     }
503 root 1.4
504     done_env_arg:
505 root 1.1 strcpy (execdir, exe_dir);
506     u_append (execdir, "i-");
507     strcat (execdir, exe_ver);
508    
509 root 1.6 strcat (strcpy (tmppath, execdir), ".lck");
510 root 1.7 lock_handle = u_lock (tmppath, 0, 1);
511 root 1.6 if (!lock_handle)
512 root 1.7 fatal ("unable to lock application instance");
513 root 1.6
514 root 1.1 if (access (execdir, F_OK))
515     {
516     // does not exist yet, so unpack and move
517     tmpdir (exe_dir);
518    
519     if (u_chdir (tmppath))
520     fatal ("unable to change to new instance directory");
521    
522     for (;;)
523     {
524     switch (pack_cur->type)
525     {
526     case T_DIR:
527     u_mkdir (PACK_NAME);
528     break;
529    
530     case T_FILE:
531     {
532     u_handle h = u_creat (PACK_NAME, pack_cur->flags & F_EXEC);
533 root 1.2 unsigned int dlen, len = u_32 (pack_cur->datalen);
534     char *data = PACK_DATA;
535    
536     if (pack_cur->flags & F_LZF)
537 root 1.6 if (dlen = lzf_decompress (data, len, scratch, scratch_size))
538 root 1.2 {
539     data = scratch;
540     len = dlen;
541     }
542     else
543     fatal ("unable to uncompress file data - pack corrupted?");
544 root 1.1
545 root 1.5 if (!u_valid (h))
546 root 1.1 fatal ("unable to unpack file from packfile - disk full?");
547    
548 root 1.2 if (u_write (h, data, len) != len)
549 root 1.1 fatal ("unable to unpack file from packfile - disk full?");
550    
551 root 1.4 u_fsync (h);
552 root 1.1 u_close (h);
553     }
554     break;
555    
556     case T_NULL:
557     goto done;
558     }
559    
560     pack_next ();
561     }
562    
563     done:
564     if (u_chdir (datadir))
565     fatal ("unable to change to data directory");
566    
567 root 1.3 u_sync ();
568    
569 root 1.1 if (u_rename (tmppath, execdir))
570     deltree (tmppath); // if move fails, delete new, assume other process created it independently
571     }
572    
573     pack_unmap ();
574     u_close (pack_handle);
575    
576 root 1.2 if (u_chdir (execdir))
577     fatal ("unable to change to application instance directory");
578    
579 root 1.1 u_setenv ("URLADER_VERSION", URLADER_VERSION);
580     u_setenv ("URLADER_DATADIR", datadir);
581     u_setenv ("URLADER_EXECDIR", execdir);
582     u_setenv ("URLADER_EXE_ID" , exe_id);
583     u_setenv ("URLADER_EXE_DIR", exe_dir);
584     u_setenv ("URLADER_EXE_VER", exe_ver);
585    
586 root 1.5 #if 0
587 root 1.1 // yes, this is overkill
588     u_setenv ("SHLIB_PATH" , execdir); // hpux
589     u_setenv ("LIBPATH" , execdir); // aix
590     u_setenv ("LD_LIBRARY_PATH" , execdir); // most elf systems
591     u_setenv ("LD_LIBRARY_PATH_32", execdir); // solaris
592     u_setenv ("LD_LIBRARY_PATH_64", execdir); // solaris
593     u_setenv ("LD_LIBRARYN32_PATH", execdir); // irix
594     u_setenv ("LD_LIBRARY64_PATH" , execdir); // irix
595     u_setenv ("DYLD_LIBRARY_PATH" , execdir); // os sucks from apple
596 root 1.5 #endif
597 root 1.1 }
598    
599 root 1.2 static void
600     execute (void)
601     {
602     exe_argv [exe_argc] = 0;
603 root 1.6 systemv (exe_argv);
604 root 1.2 }
605    
606 root 1.1 #ifdef _WIN32
607    
608     int APIENTRY
609     WinMain (HINSTANCE hI, HINSTANCE hP, LPSTR argv, int command_show)
610     {
611     if (!GetModuleFileName (hI, tmppath, sizeof (tmppath)))
612     fatal ("unable to find executable pack");
613    
614 root 1.5 pack_handle = u_open (tmppath);
615     if (!u_valid (pack_handle))
616 root 1.1 fatal ("unable to open executable pack");
617    
618     exe_info ();
619    
620     if (!GetCurrentDirectory (sizeof (currdir), currdir))
621     strcpy (currdir, ".");
622    
623     if (SHGetFolderPath (0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, datadir) != S_OK)
624     fatal ("unable to find application data directory");
625    
626     u_mkdir (datadir);
627     u_append (datadir, URLADER);
628    
629     load ();
630     execute ();
631    
632     return 0;
633     }
634    
635     #else
636    
637     int
638     main (int argc, char *argv[])
639     {
640     char *home = getenv ("HOME");
641    
642 root 1.5 pack_handle = u_open (argv [0]);
643     if (!u_valid (pack_handle))
644 root 1.1 fatal ("unable to open executable pack");
645    
646     exe_info ();
647    
648     if (!home)
649     {
650     struct passwd *pw;
651    
652     if ((pw = getpwuid (getuid ())))
653     home = pw->pw_dir;
654     else
655     home = "/tmp";
656     }
657    
658     if (!getcwd (currdir, sizeof (currdir)))
659     strcpy (currdir, ".");
660    
661     u_mkdir (home);
662 root 1.6 //strcat (strcat (strcpy (datadir, home), "/."), URLADER);
663 root 1.1 sprintf (datadir, "%s/.%s", home, URLADER);
664     u_mkdir (datadir);
665    
666 root 1.5 #if 0
667 root 1.1 if (gethostname (tmppath, sizeof (tmppath)))
668     strcpy (tmppath, "default");
669    
670     u_append (datadir, tmppath);
671 root 1.5 #endif
672 root 1.1
673     load ();
674     execute ();
675    
676     return 0;
677     }
678    
679     #endif
680