1 |
NAME |
2 |
Urlader - installer-less single-file independent executables |
3 |
|
4 |
SYNOPSIS |
5 |
use Urlader; |
6 |
|
7 |
DESCRIPTION |
8 |
Urlader (that's german for "bootloader" btw.) was created out of |
9 |
frustration over PAR always being horribly slow, again not working, |
10 |
again not being flexible enough for simple things such as software |
11 |
upgrades, and again causing mysterious missing file issues on various |
12 |
platforms. |
13 |
|
14 |
That doesn't mean this module replaces PAR, in fact, you should stay |
15 |
with PAR for many reasons right now, user-friendlyness is one of them. |
16 |
|
17 |
However, if you want to make single-file distributions out of your perl |
18 |
programs (or python, or C or whatever), and you are prepared to fiddle a |
19 |
LOT, this module might provide a faster and more versatile deployment |
20 |
technique then PAR. Well, if it ever gets finished. |
21 |
|
22 |
Also, *nothing in this module is considered very stable yet*, and it's |
23 |
far from feature-complete. |
24 |
|
25 |
Having said all that, Urlader basically provides three services: |
26 |
|
27 |
A simple archiver that packs a directory tree into a single file. |
28 |
A small C program that works on windows and unix, which unpacks an |
29 |
attached archive and runs a program (perl, python, whatever...). |
30 |
A perl module support module (*this one*), that can be used to query the |
31 |
runtime environment, find out where to install updates and so on. |
32 |
|
33 |
EXAMPLE |
34 |
How can it be used to provide single-file executables? |
35 |
|
36 |
So simple, create a directory with everything that's needed, e.g.: |
37 |
|
38 |
# find bintree |
39 |
bintree/perl |
40 |
bintree/libperl.so.5.10 |
41 |
bintree/run |
42 |
bintree/pm/Guard.pm |
43 |
bintree/pm/auto/Guard/Guard.so |
44 |
bintree/pm/XSLoader.pm |
45 |
bintree/pm/DynaLoader.pm |
46 |
bintree/pm/Config.pm |
47 |
bintree/pm/strict.pm |
48 |
bintree/pm/vars.pm |
49 |
bintree/pm/warnings.pm |
50 |
|
51 |
# cat bintree/run |
52 |
@INC = ("pm", "."); # "." works around buggy AutoLoader |
53 |
use Guard; |
54 |
guard { warn "hello, world!\n" }; # just to show off |
55 |
exit 0; # tell the urlader that everything was fine |
56 |
|
57 |
Then pack it: |
58 |
|
59 |
# wget http://urlader.schmorp.de/prebuilt/1.0/linux-x86 |
60 |
# urlader-util --urlader linux-x86 --pack myprog ver1_000 bintree \ |
61 |
LD_LIBRARY_PATH=. ./perl run \ |
62 |
>myprog |
63 |
# chmod 755 myprog |
64 |
|
65 |
The packing step takes a binary loader and appends all the files in the |
66 |
directory tree, plus some meta information. |
67 |
|
68 |
The resulting file is an executable that, when run, will unpack all the |
69 |
files and run the embedded program. |
70 |
|
71 |
CONCEPTS |
72 |
urlader |
73 |
A small (hopefully) and relatively portable (hopefully) binary that |
74 |
is prepended to a pack file to make it executable. |
75 |
|
76 |
You can build it yourself from sources (see prebuilt/Makefile in the |
77 |
distribution) or use one of the precompiled ones at: |
78 |
|
79 |
http://urlader.schmorp.de/prebuilt/1.0/ |
80 |
|
81 |
The README there has further information on the binaries provided. |
82 |
|
83 |
exe_id |
84 |
A string that uniquely identifies your program - all branches of it. |
85 |
It must consist of the characters "A-Za-z0-9_-" only and should be a |
86 |
valid directory name on all systems you want to deploy on. |
87 |
|
88 |
exe_ver |
89 |
A string the uniquely identifies the contents of the archive, i.e. |
90 |
the version. It has the same restrictions as the "exe_id", and |
91 |
should be fixed-length, as Urlader assumes lexicographically higher |
92 |
versions are newer, and thus preferable. |
93 |
|
94 |
pack file (archive) |
95 |
This contains the "exe_id", the "exe_ver", a number of environment |
96 |
variable assignments, the program name to execute, the initial |
97 |
arguments it receives, and finally, a list of files (with contents |
98 |
:) and directories. |
99 |
|
100 |
override |
101 |
When the urlader starts, it first finds out what "exe_id" is |
102 |
embedded in it. It then looks for an override file for this id |
103 |
($URLADER_EXE_DIR/override) and verifies that it is for the same |
104 |
"exe_id", and the version is newer. If this is the case, then it |
105 |
will unpack and run the override file instead of unpacking the files |
106 |
attched to itself. |
107 |
|
108 |
This way one can implement software upgrades - download a new |
109 |
executable, write it safely to disk and move it to the override |
110 |
path. |
111 |
|
112 |
ENVIRONMENT VARIABLES |
113 |
The urlader sets and maintains the following environment variables, in |
114 |
addition to any variables specified on the commandline. The values in |
115 |
parentheses are typical (but not gauranteed) values for unix - on |
116 |
windows, ~/.urlader is replaced by %AppData%/urlader. |
117 |
|
118 |
URLADER_VERSION (1.0) |
119 |
Set to the version of the urlader binary itself. All versions with |
120 |
the same major number should be compatible to older versions with |
121 |
the same major number. |
122 |
|
123 |
URLADER_DATADIR (~/.urlader) |
124 |
The data directory used to store whatever urlader needs to store. |
125 |
|
126 |
URLADER_CURRDIR |
127 |
This is set to the full path of the current working directory where |
128 |
the urlader was started. Atfer unpacking, the urlader changes to the |
129 |
"URLADER_EXECDIR", so any relative paths should be resolved via this |
130 |
path. |
131 |
|
132 |
URLADER_EXEPATH |
133 |
This is set to the path of the urlader executable itself, usually |
134 |
relative to $URLADER_CURRDIR. |
135 |
|
136 |
URLADER_EXE_ID |
137 |
This stores the executable id of the pack file attached to the |
138 |
urlader. |
139 |
|
140 |
URLADER_EXE_VER |
141 |
This is the executable version of the pack file attached to the |
142 |
urlader, or the override, whichever was newer. Or in other words, |
143 |
this is the version of the application running at the moment. |
144 |
|
145 |
URLADER_EXE_DIR (~/.urlader/$URLADER_EXE_ID> |
146 |
The directory where urlader stores files related to the executable |
147 |
with the given id. |
148 |
|
149 |
URLADER_EXECDIR (~/.urlader/$URLADER_EXE_ID/i-$URLADER_EXE_VER) |
150 |
The directory where the files from the pack file are unpacked and |
151 |
the program is being run. Also the working directory of the program |
152 |
when it is run. |
153 |
|
154 |
URLADER_OVERRIDE (empty or override) |
155 |
The override file used, if any, relative to $URLADER_EXECDIR. This |
156 |
is either missing, when no override was used, or the string |
157 |
override, as thta is currently the only override file urlader is |
158 |
looking for. |
159 |
|
160 |
FUNCTIONS AND VARIABLES IN THIS MODULE |
161 |
$Urlader::URLADER_VERSION |
162 |
Set to the urlader version ("URLADER_VERSION") when the program is |
163 |
running form within urlader, undef otherwise. |
164 |
|
165 |
$Urlader::DATADIR, $Urlader::EXE_ID, $Urlader::EXE_VER, |
166 |
$Urlader::EXE_DIR, $Urlader::EXECDIR |
167 |
Contain the same value as the environment variable of the (almost) |
168 |
same name. You should prefer these, though, as these might even be |
169 |
set to correct values when not running form within an urlader |
170 |
environment. |
171 |
|
172 |
Urlader::set_exe_info $exe_id, $exe_ver |
173 |
Sets up the paths and variables as if running the given executable |
174 |
and version from within urlader. |
175 |
|
176 |
$lock = Urlader::lock $path, $exclusive, $wait |
177 |
Tries to acquire a lock on the given path (which must specify a file |
178 |
which will be created if necessary). If $exclusive is true, then it |
179 |
tries to acquire an exclusive lock, otherwise the lock will be |
180 |
shared. If $wait is true, then it will wait until the lock can be |
181 |
acquired, otherwise it only attempts to acquire it and returns |
182 |
immediately if it can't. |
183 |
|
184 |
If successful it returns a lock object - the lock will be given up |
185 |
when the lock object is destroyed or when the process exits (even on |
186 |
a crash) and has a good chance of working on network drives as well. |
187 |
|
188 |
If the lock could not be acquired, "undef" is returned. |
189 |
|
190 |
This function is provided to assist applications that want to clean |
191 |
up old versions, see "TIPS AND TRICKS", below. |
192 |
|
193 |
TIPS AND TRICKS |
194 |
Gathering files |
195 |
Gathering all the files needed for distribution can be a big |
196 |
problem. Right now, Urlader does not assist you in this task in any |
197 |
way, however, just like perl source stripping, it is planned to |
198 |
unbundle the relevant technology from staticperl |
199 |
(<http://staticperl.plan9.de>) for use with this module. |
200 |
|
201 |
You could always use par to find all library files, unpack the |
202 |
bundle and add perl, libperl and other support libraries (e.g. |
203 |
libgcc_s). |
204 |
|
205 |
Software update |
206 |
Updating the software can be done by downloading a new packfile |
207 |
(with the same "exe_id" but a higher "exe_ver" - this can simply be |
208 |
the executable you create when making a release) and replacing the |
209 |
override file in the $URLADER_EXE_DIR. |
210 |
|
211 |
When looking for updates, you should include $URLADER_VERSION, |
212 |
$URLADER_EXE_ID and $URLADER_EXE_VER - the first two must be |
213 |
identical for update and currently running program, while the last |
214 |
one should be lexicographically higher. |
215 |
|
216 |
Replacing the override file can be done like this: |
217 |
|
218 |
rename "new-override.tmp", "$Urlader::EXE_DIR/override" |
219 |
or die "could not replace override"; |
220 |
|
221 |
This can fail on windows when another urlader currently reads it, |
222 |
but should work on all platforms even when other urlader programs |
223 |
execute concurrently. |
224 |
|
225 |
Cleaning up old directories |
226 |
Urlader only packs executables once and then caches them in the |
227 |
$URLADER_EXECDIR. After upgrades there will be old versions in there |
228 |
that are not being used anymore. Or are they? |
229 |
|
230 |
Each instance directory (i-*) in the $URLADER_EXE_DIR) has an |
231 |
associated lock file (i-*.lck) - while urlader executes an app it |
232 |
keeps a shared lock on this file. |
233 |
|
234 |
To detect whether a version is in use or not, you must try to |
235 |
acquire an exclusive lock, i.e.: |
236 |
|
237 |
my $lock = Urlader::lock "~/.urlader/myexe/i-ver01.lck", 1, 0; |
238 |
if (!$lock) { |
239 |
# instance dir is not in use and can be safely deleted |
240 |
} |
241 |
|
242 |
If an older urlader wants to use an instance that was deleted or is |
243 |
currently being deleted it will wait until it's gone and simply |
244 |
recreate it, so while less efficient, deleting instance directories |
245 |
should always be safe. |
246 |
|
247 |
The lockfile itself can be deleted as long as you have an exclusive |
248 |
lock on it (if your platform allows this). |
249 |
|
250 |
A real world project |
251 |
The only real world project using this that I know of at the moment |
252 |
is the deliantra client (http://www.deliantra.net for more info). |
253 |
|
254 |
It uses some scary scripts to build the client and some dependnet |
255 |
modules (build.*), to gather perl source files into a distribution |
256 |
tree, shared objects and system shared libraries (some of which have |
257 |
to be patched or, due to the horrible dll hell on OS X, even |
258 |
renamed), called "gatherer", and a script called "gendist" to build |
259 |
executable distributions. |
260 |
|
261 |
These can be found at |
262 |
<http://cvs.schmorp.de/deliantra/Deliantra-Client/util/>, but |
263 |
looking at them can lead to premature blindless. |
264 |
|
265 |
Shared Libraries |
266 |
It is often desirable to package shared libraries - for example the |
267 |
Deliantra client packages SD>, Berkely DB, Pango and amny other |
268 |
libraries that are unlikely to be available on the target system. |
269 |
|
270 |
This usually requires some fiddling (see below), and additionally |
271 |
some environment variables to be set. |
272 |
|
273 |
For example, on ELF systems you usually want LD_LIBRARY_PATH=. and |
274 |
on OS X, you want DYLD_LIBRARY_PATH=. (these are effectively the |
275 |
default on windows). |
276 |
|
277 |
These can most easily be specified when building the packfile: |
278 |
|
279 |
urlader-util ... LD_LIBRARY_PATH=. ./perl run |
280 |
|
281 |
Portability: RPATH |
282 |
Often perl is linked against a shared libperl.so - and might be so |
283 |
using an rpath. Perl extensikns likewise might use an rpath, which |
284 |
means the binary will mostly ignore LD_LIBRARY_PATH, which leads to |
285 |
trouble. |
286 |
|
287 |
There is an utility called chrpath, whose -d option can remove the |
288 |
rpath from binaries, shared library and shared objects. |
289 |
|
290 |
Portability: OS X DLL HELL |
291 |
OS X has the most severe form of DLL hell I have seen - if you link |
292 |
against system libraries, which is practically unavoidable, you get |
293 |
libraries of well-known names (e.g. libjpeg) that have nothing to do |
294 |
with what you normally expect libjpeg to be, and there is no way to |
295 |
get your version of libjpeg into your program. |
296 |
|
297 |
Moreover, even if apple ships well-known libraries (e.g. libiconv), |
298 |
they often ship patched versions which have a different ABI or even |
299 |
API then the real releases. |
300 |
|
301 |
The only way aorund this I found was to change all library names in |
302 |
my releases (libjpeg.dylib becomes libdeliantra-jpeg.dylin and so |
303 |
on), by patching the paths in the share dlibraries and shared |
304 |
objects. install-name-tool (with -id and -change) works in many |
305 |
cases, but often paths are embedded indirectly, so you might have to |
306 |
use a *dirty* string replacement. |
307 |
|
308 |
SECURITY CONSIDERATIONS |
309 |
The urlader executable itself does not support setuig/setgid operation, |
310 |
or running with elevated privileges - it does no input sanitisation, and |
311 |
is trivially exploitable. |
312 |
|
313 |
AUTHOR |
314 |
Marc Lehmann <schmorp@schmorp.de> |
315 |
http://home.schmorp.de/ |
316 |
|