ocran 1.4.6 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +68 -0
- data/README.md +295 -10
- data/ext/extconf.rb +5 -1
- data/lib/ocran/aot_toolchain.rb +154 -0
- data/lib/ocran/command_output.rb +8 -1
- data/lib/ocran/compatibility_scan.rb +414 -0
- data/lib/ocran/cosmo_toolchain.rb +29 -7
- data/lib/ocran/dir_builder.rb +88 -38
- data/lib/ocran/direction.rb +190 -30
- data/lib/ocran/ed_resource.rb +113 -0
- data/lib/ocran/ed_resource_builder.rb +199 -0
- data/lib/ocran/file_path_set.rb +9 -5
- data/lib/ocran/gem_spec_queryable.rb +7 -3
- data/lib/ocran/host_config_helper.rb +11 -1
- data/lib/ocran/inno_setup_script_builder.rb +24 -14
- data/lib/ocran/launcher_batch_builder.rb +8 -11
- data/lib/ocran/npm_package.rb +106 -0
- data/lib/ocran/option.rb +425 -35
- data/lib/ocran/pe_imports.rb +96 -0
- data/lib/ocran/pe_resources.rb +226 -0
- data/lib/ocran/refine_pathname.rb +29 -32
- data/lib/ocran/roundhouse_builder.rb +316 -0
- data/lib/ocran/runner.rb +43 -11
- data/lib/ocran/runtime_sdk.rb +252 -0
- data/lib/ocran/spinel_builder.rb +183 -0
- data/lib/ocran/spinel_compatibility.rb +149 -0
- data/lib/ocran/static_runtime/boot.rb +443 -0
- data/lib/ocran/static_runtime/build_static_ext.rb +86 -0
- data/lib/ocran/static_runtime_builder.rb +814 -0
- data/lib/ocran/stub_builder.rb +70 -25
- data/lib/ocran/version.rb +1 -1
- data/lib/ocran/wasm_builder.rb +570 -0
- data/lib/ocran/wasm_compatibility.rb +198 -0
- data/lib/ocran/windows_command_escaping.rb +21 -0
- data/lib/ocran/zip_payload_builder.rb +18 -9
- data/lib/ocran/zip_writer.rb +48 -9
- data/src/Makefile +18 -2
- data/src/Makefile.mswin +65 -0
- data/src/inst_dir.c +34 -6
- data/src/path_utils.c +144 -0
- data/src/script_info.c +5 -0
- data/src/stub.c +77 -19
- data/src/stub.rc +1 -1
- data/src/system_utils.c +276 -155
- data/src/system_utils.h +46 -3
- data/src/system_utils_posix.c +243 -148
- data/src/unpack.c +9 -0
- metadata +22 -4
data/src/system_utils_posix.c
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
#include <mach-o/dyld.h>
|
|
3
3
|
#endif
|
|
4
4
|
#ifdef __COSMOPOLITAN__
|
|
5
|
-
#include <cosmo.h> /* GetProgramExecutableName() */
|
|
5
|
+
#include <cosmo.h> /* GetProgramExecutableName(), IsWindows() */
|
|
6
6
|
#endif
|
|
7
7
|
#include <unistd.h>
|
|
8
8
|
#include <fcntl.h>
|
|
9
|
+
#include <termios.h>
|
|
9
10
|
#include <sys/stat.h>
|
|
10
11
|
#include <sys/mman.h>
|
|
12
|
+
#include <sys/resource.h>
|
|
11
13
|
#include <sys/wait.h>
|
|
12
14
|
#include <sys/types.h>
|
|
13
15
|
#include <dirent.h>
|
|
@@ -27,121 +29,6 @@ struct MemoryMap {
|
|
|
27
29
|
size_t size;
|
|
28
30
|
};
|
|
29
31
|
|
|
30
|
-
/* ===== Cross-platform path utilities (from system_utils.c) ===== */
|
|
31
|
-
|
|
32
|
-
bool IsCleanRelativePath(const char *path)
|
|
33
|
-
{
|
|
34
|
-
if (!path || !*path) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#ifdef _WIN32
|
|
39
|
-
/* Forbid Windows drive specification (e.g. "C:\") */
|
|
40
|
-
if (((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
|
|
41
|
-
&& path[1] == ':'
|
|
42
|
-
&& is_path_separator(path[2])) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
#endif
|
|
46
|
-
|
|
47
|
-
/* Forbid absolute path (leading '/' or '\') */
|
|
48
|
-
if (is_path_separator(*path)) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* Validate each path segment */
|
|
53
|
-
const char *p = path;
|
|
54
|
-
while (*p) {
|
|
55
|
-
const char *start = p;
|
|
56
|
-
|
|
57
|
-
/* Advance until next separator or end-of-string */
|
|
58
|
-
while (*p && !is_path_separator(*p)) {
|
|
59
|
-
p++;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
size_t len = p - start;
|
|
63
|
-
|
|
64
|
-
/* Reject empty, "." or ".." segments */
|
|
65
|
-
if (len == 0
|
|
66
|
-
|| (len == 1 && start[0] == '.')
|
|
67
|
-
|| (len == 2 && start[0] == '.' && start[1] == '.')) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* Skip over the separator */
|
|
72
|
-
if (*p) {
|
|
73
|
-
p++;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
char *JoinPath(const char *p1, const char *p2)
|
|
81
|
-
{
|
|
82
|
-
if (p1 == NULL || *p1 == '\0') {
|
|
83
|
-
APP_ERROR("p1 is null or empty");
|
|
84
|
-
return NULL;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (p2 == NULL || *p2 == '\0') {
|
|
88
|
-
APP_ERROR("p2 is null or empty");
|
|
89
|
-
return NULL;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
size_t p1_len = strlen(p1);
|
|
93
|
-
if (is_path_separator(p1[p1_len - 1])) { p1_len--; }
|
|
94
|
-
|
|
95
|
-
size_t p2_len = strlen(p2);
|
|
96
|
-
const char *p2_start = p2;
|
|
97
|
-
if (is_path_separator(*p2_start)) { p2_start++; p2_len--; }
|
|
98
|
-
|
|
99
|
-
size_t joined_len = p1_len + 1 + p2_len;
|
|
100
|
-
char *joined_path = calloc(1, joined_len + 1);
|
|
101
|
-
if (!joined_path) {
|
|
102
|
-
APP_ERROR("Failed to allocate buffer for join path");
|
|
103
|
-
return NULL;
|
|
104
|
-
}
|
|
105
|
-
memcpy(joined_path, p1, p1_len);
|
|
106
|
-
joined_path[p1_len] = PATH_SEPARATOR;
|
|
107
|
-
memcpy(joined_path + p1_len + 1, p2_start, p2_len);
|
|
108
|
-
joined_path[joined_len] = '\0';
|
|
109
|
-
|
|
110
|
-
return joined_path;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
char *GetParentPath(const char *path)
|
|
114
|
-
{
|
|
115
|
-
if (!path) {
|
|
116
|
-
APP_ERROR("path is NULL");
|
|
117
|
-
return NULL;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
size_t len = strlen(path);
|
|
121
|
-
size_t i = len;
|
|
122
|
-
|
|
123
|
-
/* Skip any trailing separators */
|
|
124
|
-
while (i > 0 && is_path_separator(path[i])) {
|
|
125
|
-
i--;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* Skip the last segment's characters */
|
|
129
|
-
while (i > 0 && !is_path_separator(path[i])) {
|
|
130
|
-
i--;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/* i==0 ⇒ empty parent */
|
|
134
|
-
|
|
135
|
-
char *out = malloc(i + 1);
|
|
136
|
-
if (!out) {
|
|
137
|
-
APP_ERROR("Memory allocation failed for parent path");
|
|
138
|
-
return NULL;
|
|
139
|
-
}
|
|
140
|
-
memcpy(out, path, i);
|
|
141
|
-
out[i] = '\0';
|
|
142
|
-
return out;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
32
|
/* ===== Memory-mapped file I/O ===== */
|
|
146
33
|
|
|
147
34
|
MemoryMap *CreateMemoryMap(const char *path) {
|
|
@@ -248,6 +135,9 @@ bool CreateDirectoriesRecursively(const char *dir) {
|
|
|
248
135
|
return true;
|
|
249
136
|
}
|
|
250
137
|
|
|
138
|
+
/* Keeps going after a failure so that as much as possible gets deleted, and
|
|
139
|
+
returns false if anything could not be. Failures are only reported in
|
|
140
|
+
debug mode: the application has run, and there is nobody to act on them. */
|
|
251
141
|
bool DeleteRecursively(const char *path) {
|
|
252
142
|
if (!path || !*path) {
|
|
253
143
|
return false;
|
|
@@ -255,23 +145,31 @@ bool DeleteRecursively(const char *path) {
|
|
|
255
145
|
|
|
256
146
|
struct stat st;
|
|
257
147
|
if (lstat(path, &st) < 0) {
|
|
258
|
-
|
|
148
|
+
APP_ERROR("DeleteRecursively: lstat(\"%s\") failed: %s", path, strerror(errno));
|
|
259
149
|
return false;
|
|
260
150
|
}
|
|
261
151
|
|
|
262
152
|
if (!S_ISDIR(st.st_mode)) {
|
|
263
|
-
/*
|
|
153
|
+
/* A file or a symlink (which is not followed): just delete it */
|
|
264
154
|
if (unlink(path) < 0) {
|
|
265
|
-
|
|
155
|
+
APP_ERROR("DeleteRecursively: unlink(\"%s\") failed: %s", path, strerror(errno));
|
|
266
156
|
return false;
|
|
267
157
|
}
|
|
268
158
|
return true;
|
|
269
159
|
}
|
|
270
160
|
|
|
161
|
+
/* Listing a directory and deleting its entries takes read, write and
|
|
162
|
+
search permission, which the application may have removed (e.g. a
|
|
163
|
+
read-only copy of a Go module cache). The directory is the user's
|
|
164
|
+
own, so give them back. */
|
|
165
|
+
if ((st.st_mode & S_IRWXU) != S_IRWXU) {
|
|
166
|
+
chmod(path, (st.st_mode & 07777) | S_IRWXU);
|
|
167
|
+
}
|
|
168
|
+
|
|
271
169
|
/* It's a directory, delete contents recursively */
|
|
272
170
|
DIR *dir = opendir(path);
|
|
273
171
|
if (!dir) {
|
|
274
|
-
|
|
172
|
+
APP_ERROR("DeleteRecursively: opendir(\"%s\") failed: %s", path, strerror(errno));
|
|
275
173
|
return false;
|
|
276
174
|
}
|
|
277
175
|
|
|
@@ -285,13 +183,11 @@ bool DeleteRecursively(const char *path) {
|
|
|
285
183
|
char *child_path = JoinPath(path, entry->d_name);
|
|
286
184
|
if (!child_path) {
|
|
287
185
|
success = false;
|
|
288
|
-
|
|
186
|
+
continue;
|
|
289
187
|
}
|
|
290
188
|
|
|
291
189
|
if (!DeleteRecursively(child_path)) {
|
|
292
|
-
free(child_path);
|
|
293
190
|
success = false;
|
|
294
|
-
break;
|
|
295
191
|
}
|
|
296
192
|
free(child_path);
|
|
297
193
|
}
|
|
@@ -300,7 +196,7 @@ bool DeleteRecursively(const char *path) {
|
|
|
300
196
|
|
|
301
197
|
if (success) {
|
|
302
198
|
if (rmdir(path) < 0) {
|
|
303
|
-
|
|
199
|
+
APP_ERROR("DeleteRecursively: rmdir(\"%s\") failed: %s", path, strerror(errno));
|
|
304
200
|
return false;
|
|
305
201
|
}
|
|
306
202
|
}
|
|
@@ -338,8 +234,13 @@ bool ExportFile(const char *path, const void *buffer, size_t buffer_size) {
|
|
|
338
234
|
free(parent);
|
|
339
235
|
}
|
|
340
236
|
|
|
341
|
-
/* Create/overwrite the file
|
|
342
|
-
|
|
237
|
+
/* Create/overwrite the file, but never write through a symlink that
|
|
238
|
+
happens to sit at its path (the file would land at the link's
|
|
239
|
+
target). O_EXCL would be stricter still, but on a case-insensitive
|
|
240
|
+
file system (the macOS default) it would turn two packed names that
|
|
241
|
+
differ only in case, which merely overwrite each other today, into a
|
|
242
|
+
failed start. */
|
|
243
|
+
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0777);
|
|
343
244
|
if (fd < 0) {
|
|
344
245
|
FATAL("ExportFile: open(\"%s\") failed: %s", path, strerror(errno));
|
|
345
246
|
return false;
|
|
@@ -449,28 +350,191 @@ char *GetTempDirectoryPath(void) {
|
|
|
449
350
|
|
|
450
351
|
/* ===== Process and signal handling ===== */
|
|
451
352
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
353
|
+
/*
|
|
354
|
+
* Termination signals the stub handles while it runs. The handler records
|
|
355
|
+
* the signal and passes it on to the child, so that `kill`, `docker stop`
|
|
356
|
+
* or a service manager stopping the stub stop the application, and the
|
|
357
|
+
* stub lives on to delete the extraction directory. Signals that were
|
|
358
|
+
* ignored when the stub started (e.g. SIGHUP under nohup) stay ignored,
|
|
359
|
+
* and the child inherits that.
|
|
360
|
+
*/
|
|
361
|
+
static const int TerminationSignals[] = { SIGINT, SIGTERM, SIGHUP, SIGQUIT };
|
|
362
|
+
#define TERMINATION_SIGNAL_COUNT \
|
|
363
|
+
(sizeof(TerminationSignals) / sizeof(TerminationSignals[0]))
|
|
364
|
+
|
|
365
|
+
/* Which of TerminationSignals were ignored at startup. */
|
|
366
|
+
static bool SignalIgnoredAtStartup[TERMINATION_SIGNAL_COUNT];
|
|
367
|
+
|
|
368
|
+
/* Child to forward termination signals to; 0 while there is none. */
|
|
369
|
+
static volatile sig_atomic_t ChildPid = 0;
|
|
370
|
+
|
|
371
|
+
/* Last termination signal the stub received itself. */
|
|
372
|
+
static volatile sig_atomic_t ReceivedSignal = 0;
|
|
373
|
+
|
|
374
|
+
/* Signal the stub dies of once cleanup is done: the one that killed the
|
|
375
|
+
child, or one received before the child was started. */
|
|
376
|
+
static int TerminatingSignal = 0;
|
|
377
|
+
|
|
378
|
+
/* Whether a signal was sent by a process (kill(2), sigqueue(3)) rather than
|
|
379
|
+
generated by the kernel. Signals the kernel raises for a terminal (Ctrl+C,
|
|
380
|
+
Ctrl+\, hangup) go to the whole foreground process group, the child
|
|
381
|
+
included, which must not get them twice. */
|
|
382
|
+
#ifdef __APPLE__
|
|
383
|
+
/* Whether this process is in the foreground process group of its
|
|
384
|
+
controlling terminal, the only place a terminal-generated signal can
|
|
385
|
+
reach it. Everything here is async-signal-safe. */
|
|
386
|
+
static bool in_foreground_process_group(void)
|
|
387
|
+
{
|
|
388
|
+
int fd = open("/dev/tty", O_RDONLY | O_NOCTTY | O_CLOEXEC);
|
|
389
|
+
if (fd < 0) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
pid_t foreground = tcgetpgrp(fd);
|
|
393
|
+
close(fd);
|
|
394
|
+
return foreground > 0 && foreground == getpgrp();
|
|
395
|
+
}
|
|
396
|
+
#endif
|
|
397
|
+
|
|
398
|
+
static bool is_sent_by_process(const siginfo_t *info)
|
|
399
|
+
{
|
|
400
|
+
if (!info) {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
if (info->si_code == SI_USER) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
#ifdef __APPLE__
|
|
407
|
+
/* XNU never reports SI_USER: every caught signal arrives with si_code 0
|
|
408
|
+
and si_pid naming the process in whose context it was raised, for
|
|
409
|
+
kill(2) and for the terminal driver alike (bsd/kern/kern_sig.c), so
|
|
410
|
+
siginfo cannot tell them apart. A terminal signal only reaches the
|
|
411
|
+
foreground process group of the controlling terminal; a stub running
|
|
412
|
+
anywhere else - no terminal, a background job, a service, a CI runner
|
|
413
|
+
- can only have been signaled by a process. */
|
|
414
|
+
if (info->si_code == 0) {
|
|
415
|
+
return !in_foreground_process_group();
|
|
416
|
+
}
|
|
417
|
+
#endif
|
|
418
|
+
#ifdef SI_QUEUE
|
|
419
|
+
if (info->si_code == SI_QUEUE) {
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
#endif
|
|
423
|
+
#ifdef SI_TKILL
|
|
424
|
+
if (info->si_code == SI_TKILL) {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
#endif
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
static void termination_signal_handler(int sig, siginfo_t *info, void *context)
|
|
432
|
+
{
|
|
433
|
+
(void)context;
|
|
434
|
+
int saved_errno = errno;
|
|
456
435
|
|
|
436
|
+
ReceivedSignal = sig;
|
|
437
|
+
pid_t child = (pid_t)ChildPid;
|
|
438
|
+
if (child > 0 && is_sent_by_process(info)) {
|
|
439
|
+
kill(child, sig);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
errno = saved_errno;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
bool InitializeSignalHandling(void) {
|
|
457
446
|
struct sigaction sa;
|
|
458
447
|
memset(&sa, 0, sizeof(sa));
|
|
459
|
-
sa.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
448
|
+
sa.sa_sigaction = termination_signal_handler;
|
|
449
|
+
sa.sa_flags = SA_SIGINFO | SA_RESTART;
|
|
450
|
+
sigemptyset(&sa.sa_mask);
|
|
451
|
+
for (size_t i = 0; i < TERMINATION_SIGNAL_COUNT; i++) {
|
|
452
|
+
sigaddset(&sa.sa_mask, TerminationSignals[i]);
|
|
464
453
|
}
|
|
465
454
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
455
|
+
for (size_t i = 0; i < TERMINATION_SIGNAL_COUNT; i++) {
|
|
456
|
+
int sig = TerminationSignals[i];
|
|
457
|
+
struct sigaction old;
|
|
458
|
+
|
|
459
|
+
if (sigaction(sig, NULL, &old) < 0) {
|
|
460
|
+
FATAL("InitializeSignalHandling: sigaction(%d) failed: %s", sig, strerror(errno));
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
if (!(old.sa_flags & SA_SIGINFO) && old.sa_handler == SIG_IGN) {
|
|
464
|
+
SignalIgnoredAtStartup[i] = true;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (sigaction(sig, &sa, NULL) < 0) {
|
|
468
|
+
FATAL("InitializeSignalHandling: sigaction(%d) failed: %s", sig, strerror(errno));
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
469
471
|
}
|
|
470
472
|
|
|
471
473
|
return true;
|
|
472
474
|
}
|
|
473
475
|
|
|
476
|
+
static void (*CleanupRoutine)(void) = NULL;
|
|
477
|
+
static bool CleanupDone = false;
|
|
478
|
+
|
|
479
|
+
void SetCleanupRoutine(void (*routine)(void))
|
|
480
|
+
{
|
|
481
|
+
CleanupRoutine = routine;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* Only ever called from the main thread here: signal handlers merely
|
|
485
|
+
forward, and the main thread outlives the child to clean up. */
|
|
486
|
+
void RunCleanupRoutine(void)
|
|
487
|
+
{
|
|
488
|
+
if (CleanupDone) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
CleanupDone = true;
|
|
492
|
+
if (CleanupRoutine) {
|
|
493
|
+
CleanupRoutine();
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
void ReraiseChildSignal(void)
|
|
498
|
+
{
|
|
499
|
+
int sig = TerminatingSignal;
|
|
500
|
+
if (sig == 0) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
#ifdef __COSMOPOLITAN__
|
|
505
|
+
/* A native Windows parent cannot see a signal death; it gets the
|
|
506
|
+
128 + signal exit code instead (see the end of main()). */
|
|
507
|
+
if (IsWindows()) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
#endif
|
|
511
|
+
|
|
512
|
+
DEBUG("Terminating with signal %d like the application", sig);
|
|
513
|
+
fflush(NULL);
|
|
514
|
+
|
|
515
|
+
/* The application dumped core already if it was going to. */
|
|
516
|
+
struct rlimit no_core = { 0, 0 };
|
|
517
|
+
setrlimit(RLIMIT_CORE, &no_core);
|
|
518
|
+
|
|
519
|
+
struct sigaction sa;
|
|
520
|
+
memset(&sa, 0, sizeof(sa));
|
|
521
|
+
sa.sa_handler = SIG_DFL;
|
|
522
|
+
sigemptyset(&sa.sa_mask);
|
|
523
|
+
sigaction(sig, &sa, NULL);
|
|
524
|
+
|
|
525
|
+
sigset_t set;
|
|
526
|
+
sigemptyset(&set);
|
|
527
|
+
sigaddset(&set, sig);
|
|
528
|
+
sigprocmask(SIG_UNBLOCK, &set, NULL);
|
|
529
|
+
|
|
530
|
+
raise(sig);
|
|
531
|
+
|
|
532
|
+
/* Still alive: e.g. PID 1 of a container, for which the kernel drops
|
|
533
|
+
signals whose action is the default. The caller exits with
|
|
534
|
+
128 + signal instead. */
|
|
535
|
+
DEBUG("Signal %d did not terminate the stub", sig);
|
|
536
|
+
}
|
|
537
|
+
|
|
474
538
|
bool SetEnvVar(const char *name, const char *value) {
|
|
475
539
|
if (!name) {
|
|
476
540
|
FATAL("SetEnvVar: name is NULL");
|
|
@@ -499,31 +563,60 @@ bool CreateAndWaitForProcess(const char *app_name, char *argv[], int *exit_code)
|
|
|
499
563
|
return false;
|
|
500
564
|
}
|
|
501
565
|
|
|
566
|
+
/* Hold termination signals until ChildPid is set, so that none arriving
|
|
567
|
+
around fork() gets lost. */
|
|
568
|
+
sigset_t term_set, orig_mask;
|
|
569
|
+
sigemptyset(&term_set);
|
|
570
|
+
for (size_t i = 0; i < TERMINATION_SIGNAL_COUNT; i++) {
|
|
571
|
+
sigaddset(&term_set, TerminationSignals[i]);
|
|
572
|
+
}
|
|
573
|
+
sigprocmask(SIG_BLOCK, &term_set, &orig_mask);
|
|
574
|
+
|
|
575
|
+
/* Asked to terminate during extraction: do not start the application. */
|
|
576
|
+
if (ReceivedSignal) {
|
|
577
|
+
TerminatingSignal = ReceivedSignal;
|
|
578
|
+
*exit_code = 128 + TerminatingSignal;
|
|
579
|
+
DEBUG("Received signal %d before the application started", TerminatingSignal);
|
|
580
|
+
sigprocmask(SIG_SETMASK, &orig_mask, NULL);
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
|
|
502
584
|
pid_t pid = fork();
|
|
503
585
|
if (pid < 0) {
|
|
504
586
|
FATAL("CreateAndWaitForProcess: fork() failed: %s", strerror(errno));
|
|
587
|
+
sigprocmask(SIG_SETMASK, &orig_mask, NULL);
|
|
505
588
|
return false;
|
|
506
589
|
}
|
|
507
590
|
|
|
508
591
|
if (pid == 0) {
|
|
509
|
-
/* Child process
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
592
|
+
/* Child process: restore the signal handling the stub was started
|
|
593
|
+
with, then execute the target application. */
|
|
594
|
+
for (size_t i = 0; i < TERMINATION_SIGNAL_COUNT; i++) {
|
|
595
|
+
signal(TerminationSignals[i],
|
|
596
|
+
SignalIgnoredAtStartup[i] ? SIG_IGN : SIG_DFL);
|
|
597
|
+
}
|
|
598
|
+
sigprocmask(SIG_SETMASK, &orig_mask, NULL);
|
|
514
599
|
|
|
515
|
-
/* Execute the target application */
|
|
516
600
|
execv(app_name, argv);
|
|
517
601
|
|
|
518
|
-
/* If we get here, execv failed
|
|
602
|
+
/* If we get here, execv failed. _exit, not exit: atexit handlers
|
|
603
|
+
and stdio buffers belong to the parent. */
|
|
519
604
|
FATAL("CreateAndWaitForProcess: execv(\"%s\") failed: %s", app_name, strerror(errno));
|
|
520
|
-
|
|
605
|
+
_exit(127);
|
|
521
606
|
}
|
|
522
607
|
|
|
523
608
|
/* Parent process */
|
|
609
|
+
ChildPid = pid;
|
|
610
|
+
sigprocmask(SIG_SETMASK, &orig_mask, NULL);
|
|
524
611
|
|
|
525
612
|
int wstatus;
|
|
526
|
-
|
|
613
|
+
pid_t waited;
|
|
614
|
+
do {
|
|
615
|
+
waited = waitpid(pid, &wstatus, 0);
|
|
616
|
+
} while (waited < 0 && errno == EINTR);
|
|
617
|
+
ChildPid = 0;
|
|
618
|
+
|
|
619
|
+
if (waited < 0) {
|
|
527
620
|
FATAL("CreateAndWaitForProcess: waitpid failed: %s", strerror(errno));
|
|
528
621
|
return false;
|
|
529
622
|
}
|
|
@@ -531,7 +624,9 @@ bool CreateAndWaitForProcess(const char *app_name, char *argv[], int *exit_code)
|
|
|
531
624
|
if (WIFEXITED(wstatus)) {
|
|
532
625
|
*exit_code = WEXITSTATUS(wstatus);
|
|
533
626
|
} else if (WIFSIGNALED(wstatus)) {
|
|
534
|
-
|
|
627
|
+
TerminatingSignal = WTERMSIG(wstatus);
|
|
628
|
+
*exit_code = 128 + TerminatingSignal;
|
|
629
|
+
DEBUG("The application was terminated by signal %d", TerminatingSignal);
|
|
535
630
|
} else {
|
|
536
631
|
*exit_code = 1;
|
|
537
632
|
}
|
data/src/unpack.c
CHANGED
|
@@ -224,6 +224,15 @@ static bool decompress_lzma(void *dest, unsigned long long dest_size,
|
|
|
224
224
|
APP_ERROR("LZMA decompression error: %d, status: %d", res, status);
|
|
225
225
|
return false;
|
|
226
226
|
}
|
|
227
|
+
|
|
228
|
+
/* The end mark can come before the buffer is full: the rest of it would
|
|
229
|
+
then be parsed as opcodes although it holds nothing but whatever
|
|
230
|
+
malloc left there. */
|
|
231
|
+
if ((unsigned long long)decompressed_size != dest_size) {
|
|
232
|
+
APP_ERROR("LZMA decompressed size mismatch: header says %llu bytes, got %llu",
|
|
233
|
+
dest_size, (unsigned long long)decompressed_size);
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
227
236
|
return true;
|
|
228
237
|
}
|
|
229
238
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocran
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andi Idogawa
|
|
@@ -30,10 +30,10 @@ description: 'OCRAN (One-Click Ruby Application Next) packages Ruby applications
|
|
|
30
30
|
gems, and native libraries into a self-contained artifact that runs without Ruby
|
|
31
31
|
installed on the target machine: a self-extracting executable (default), a directory
|
|
32
32
|
(--output-dir), a zip archive (--output-zip), or a macOS .app bundle (--macosx-bundle).
|
|
33
|
-
Fork of OCRA maintained for Ruby 3.
|
|
33
|
+
Fork of OCRA maintained for Ruby 3.3+; migrating code should replace OCRA_EXECUTABLE
|
|
34
34
|
with OCRAN_EXECUTABLE.'
|
|
35
35
|
email:
|
|
36
|
-
-
|
|
36
|
+
- web@idogawa.com
|
|
37
37
|
executables:
|
|
38
38
|
- ocran
|
|
39
39
|
extensions:
|
|
@@ -46,14 +46,18 @@ files:
|
|
|
46
46
|
- exe/ocran
|
|
47
47
|
- ext/extconf.rb
|
|
48
48
|
- lib/ocran.rb
|
|
49
|
+
- lib/ocran/aot_toolchain.rb
|
|
49
50
|
- lib/ocran/build_constants.rb
|
|
50
51
|
- lib/ocran/build_facade.rb
|
|
51
52
|
- lib/ocran/build_helper.rb
|
|
52
53
|
- lib/ocran/command_output.rb
|
|
54
|
+
- lib/ocran/compatibility_scan.rb
|
|
53
55
|
- lib/ocran/cosmo_toolchain.rb
|
|
54
56
|
- lib/ocran/dir_builder.rb
|
|
55
57
|
- lib/ocran/direction.rb
|
|
56
58
|
- lib/ocran/ed_icon.rb
|
|
59
|
+
- lib/ocran/ed_resource.rb
|
|
60
|
+
- lib/ocran/ed_resource_builder.rb
|
|
57
61
|
- lib/ocran/empty_source
|
|
58
62
|
- lib/ocran/file_path_set.rb
|
|
59
63
|
- lib/ocran/gem_spec_queryable.rb
|
|
@@ -63,18 +67,31 @@ files:
|
|
|
63
67
|
- lib/ocran/launcher_event_recorder.rb
|
|
64
68
|
- lib/ocran/library_detector.rb
|
|
65
69
|
- lib/ocran/library_detector_posix.rb
|
|
70
|
+
- lib/ocran/npm_package.rb
|
|
66
71
|
- lib/ocran/option.rb
|
|
72
|
+
- lib/ocran/pe_imports.rb
|
|
73
|
+
- lib/ocran/pe_resources.rb
|
|
67
74
|
- lib/ocran/refine_pathname.rb
|
|
75
|
+
- lib/ocran/roundhouse_builder.rb
|
|
68
76
|
- lib/ocran/rubyopt_processor.rb
|
|
69
77
|
- lib/ocran/runner.rb
|
|
70
78
|
- lib/ocran/runtime_environment.rb
|
|
79
|
+
- lib/ocran/runtime_sdk.rb
|
|
80
|
+
- lib/ocran/spinel_builder.rb
|
|
81
|
+
- lib/ocran/spinel_compatibility.rb
|
|
82
|
+
- lib/ocran/static_runtime/boot.rb
|
|
83
|
+
- lib/ocran/static_runtime/build_static_ext.rb
|
|
84
|
+
- lib/ocran/static_runtime_builder.rb
|
|
71
85
|
- lib/ocran/stub_builder.rb
|
|
72
86
|
- lib/ocran/version.rb
|
|
87
|
+
- lib/ocran/wasm_builder.rb
|
|
88
|
+
- lib/ocran/wasm_compatibility.rb
|
|
73
89
|
- lib/ocran/windows_command_escaping.rb
|
|
74
90
|
- lib/ocran/zip_payload_builder.rb
|
|
75
91
|
- lib/ocran/zip_writer.rb
|
|
76
92
|
- share/ocran/lzma.exe
|
|
77
93
|
- src/Makefile
|
|
94
|
+
- src/Makefile.mswin
|
|
78
95
|
- src/error.c
|
|
79
96
|
- src/error.h
|
|
80
97
|
- src/inst_dir.c
|
|
@@ -84,6 +101,7 @@ files:
|
|
|
84
101
|
- src/lzma/LzmaDec.c
|
|
85
102
|
- src/lzma/LzmaDec.h
|
|
86
103
|
- src/lzma/Precomp.h
|
|
104
|
+
- src/path_utils.c
|
|
87
105
|
- src/script_info.c
|
|
88
106
|
- src/script_info.h
|
|
89
107
|
- src/stub.c
|
|
@@ -109,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
109
127
|
requirements:
|
|
110
128
|
- - ">="
|
|
111
129
|
- !ruby/object:Gem::Version
|
|
112
|
-
version: 3.
|
|
130
|
+
version: 3.3.0
|
|
113
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
132
|
requirements:
|
|
115
133
|
- - ">="
|