forked from holzschu/ios_system
-
Notifications
You must be signed in to change notification settings - Fork 2
/
libc_replacement.c
715 lines (659 loc) · 24.9 KB
/
libc_replacement.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
//
// libc_replacement.c
// ios_system
//
// Created by Nicolas Holzschuch on 30/04/2018.
// Copyright © 2018 Nicolas Holzschuch. All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <dlfcn.h> // for dlopen()/dlsym()/dlclose()
#include "ios_error.h"
#undef write
#undef fwrite
#undef puts
#undef fputs
#undef fputc
#undef putw
#undef putp
#undef fflush
#undef getenv
#undef setenv
#undef unsetenv
// in order to run webAssembly commands sequentially, we first stack them, then run them in command line order:
// At this point, this could just be a mutex.
int preparingWebAssemblyCommands = 0;
int orderOfWebAssemblyCommands = 0;
void startedPreparingWebAssemblyCommand(void) {
preparingWebAssemblyCommands += 1;
orderOfWebAssemblyCommands += 1;
}
int webAssemblyCommandOrder(void) {
return orderOfWebAssemblyCommands;
}
void finishedPreparingWebAssemblyCommand(void) {
if (preparingWebAssemblyCommands > 0)
preparingWebAssemblyCommands -= 1;
}
static void executeWebAssemblyCommandsInOrder(void) {
void (*function)(void) = NULL;
function = dlsym(RTLD_MAIN_ONLY, "executeWebAssemblyCommands");
if (function != NULL) {
while (preparingWebAssemblyCommands > 0) {
// Empty loops create problems in Release mode.
if (thread_stdout != NULL) { fflush(thread_stdout); }
if (thread_stderr != NULL) { fflush(thread_stderr); }
}
function();
}
orderOfWebAssemblyCommands = 0;
}
int printf (const char *format, ...) {
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (thread_stdout, format, arg);
va_end (arg);
return done;
}
int fprintf(FILE * restrict stream, const char * restrict format, ...) {
va_list arg;
int done;
if (thread_stderr == NULL) thread_stderr = stderr;
if (thread_stdout == NULL) thread_stdout = stdout;
va_start (arg, format);
if (fileno(stream) == STDOUT_FILENO) done = vfprintf (thread_stdout, format, arg);
else if (fileno(stream) == STDERR_FILENO) done = vfprintf (thread_stderr, format, arg);
// iOS, debug:
// else if ((fileno(stream) == STDERR_FILENO) || (fileno(stream) == fileno(thread_stderr))) done = vfprintf (stderr, format, arg);
else done = vfprintf (stream, format, arg);
va_end (arg);
return done;
}
int scanf (const char *format, ...) {
int count;
va_list ap;
if (thread_stderr == NULL) thread_stderr = stderr;
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stdin == NULL) thread_stdin = stdin;
fflush(thread_stdout);
fflush(thread_stderr);
va_start (ap, format);
count = vfscanf (thread_stdin, format, ap);
va_end (ap);
return (count);
}
int ios_fflush(FILE *stream) {
if (stream == NULL) return 0;
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fileno(stream) == STDOUT_FILENO) return fflush(thread_stdout);
if (fileno(stream) == STDERR_FILENO) return fflush(thread_stderr);
return fflush(stream);
}
ssize_t ios_write(int fildes, const void *buf, size_t nbyte) {
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fildes == STDOUT_FILENO) return write(fileno(thread_stdout), buf, nbyte);
if (fildes == STDERR_FILENO) return write(fileno(thread_stderr), buf, nbyte);
return write(fildes, buf, nbyte);
}
size_t ios_fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream) {
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fileno(stream) == STDOUT_FILENO) return fwrite(ptr, size, nitems, thread_stdout);
if (fileno(stream) == STDERR_FILENO) return fwrite(ptr, size, nitems, thread_stderr);
return fwrite(ptr, size, nitems, stream);
}
int ios_puts(const char *s) {
if (thread_stdout == NULL) thread_stdout = stdout;
// puts adds a newline at the end.
int returnValue = fputs(s, thread_stdout);
fputc('\n', thread_stdout);
return returnValue;
}
int ios_fputs(const char* s, FILE *stream) {
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fileno(stream) == STDOUT_FILENO) return fputs(s, thread_stdout);
if (fileno(stream) == STDERR_FILENO) return fputs(s, thread_stderr);
return fputs(s, stream);
}
int ios_fputc(int c, FILE *stream) {
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fileno(stream) == STDOUT_FILENO) return fputc(c, thread_stdout);
if (fileno(stream) == STDERR_FILENO) return fputc(c, thread_stderr);
return fputc(c, stream);
}
#include <assert.h>
int ios_putw(int w, FILE *stream) {
if (thread_stdout == NULL) thread_stdout = stdout;
if (thread_stderr == NULL) thread_stderr = stderr;
if (fileno(stream) == STDOUT_FILENO) return putw(w, thread_stdout);
if (fileno(stream) == STDERR_FILENO) return putw(w, thread_stderr);
return putw(w, stream);
}
// Fake process IDs to go with fake forking:
// You will still need to edit your code to make sure you go through both branches.
#define IOS_MAX_THREADS 128
static pthread_t thread_ids[IOS_MAX_THREADS];
static int numVariablesSet[IOS_MAX_THREADS];
static char** environment[IOS_MAX_THREADS];
static char** copyEnvironment[IOS_MAX_THREADS];
static char previousDirectory[IOS_MAX_THREADS][MAXPATHLEN];
static int previousPid[IOS_MAX_THREADS];
static int pid_overflow = 0;
static pid_t current_pid = 0;
// We need to lock current_pid during operations
pthread_mutex_t pid_mtx = PTHREAD_MUTEX_INITIALIZER;
_Atomic(int) cleanup_counter = 0;
static pid_t last_allocated_pid = 0;
void makeGlobal(void) {
copyEnvironment[current_pid] = environment[current_pid];
environment[current_pid] = NULL; // makes it really global
}
void makeLocal(void) {
environment[current_pid] = copyEnvironment[current_pid];
copyEnvironment[current_pid] = NULL;
}
inline pthread_t ios_getThreadId(pid_t pid) {
// return ios_getLastThreadId(); // previous behaviour
if (pid >= IOS_MAX_THREADS) { return -1; }
return thread_ids[pid];
}
void newPreviousDirectory(void) {
// Called when a command calls "cd". Actually changes the directory for that command.
getwd(previousDirectory[current_pid]);
}
// We do not recycle process ids too quickly to avoid collisions.
void storeEnvironment(char* envp[]);
static inline const pid_t ios_nextAvailablePid(void) {
while (cleanup_counter > 0) { } // Don't start a command while another is ending.
// fprintf(stderr, "Locking in ios_nextAvailablePid\n");
pthread_mutex_lock(&pid_mtx);
char** currentEnvironment = environmentVariables(current_pid);
int previousPidId = current_pid;
if (!pid_overflow && (last_allocated_pid < IOS_MAX_THREADS - 1)
&& (thread_ids[last_allocated_pid+1] == 0)) {
current_pid = last_allocated_pid + 1;
last_allocated_pid = current_pid;
thread_ids[current_pid] = -1; // Not yet started
numVariablesSet[current_pid] = 0;
environment[current_pid] = NULL;
storeEnvironment(currentEnvironment); // duplicate the environment variables
getwd(previousDirectory[current_pid]); // store current working directory
previousPid[current_pid] = previousPidId;
// fprintf(stderr, "Returning from ios_nextAvailablePid, pid= %d\n", current_pid);
return current_pid;
}
// We've already started more than IOS_MAX_THREADS threads.
if (!pid_overflow) current_pid = 0; // first time over the limit
pid_overflow = 1;
while (1) {
current_pid = last_allocated_pid + 1;
last_allocated_pid = current_pid;
if (current_pid >= IOS_MAX_THREADS) {
current_pid = 1;
last_allocated_pid = 1;
}
pthread_t thread_pid = ios_getThreadId(current_pid);
if (thread_pid == 0) { // We found a not-active pid
thread_ids[current_pid] = -1; // Not yet started
numVariablesSet[current_pid] = 0;
environment[current_pid] = NULL;
storeEnvironment(currentEnvironment); // duplicate the environment variables
getwd(previousDirectory[current_pid]); // store current working directory
previousPid[current_pid] = previousPidId;
// fprintf(stderr, "Returning from ios_nextAvailablePid, pid= %d\n", current_pid);
return current_pid;
}
// Dangerous: if the process is already killed, this wil crash
/*
if (pthread_kill(thread_pid, 0) != 0) {
thread_ids[current_pid] = 0;
return current_pid; // not running anymore
}
*/
}
}
inline void ios_storeThreadId(pthread_t thread) {
// To avoid issues when a command starts a command without forking,
// we only store thread IDs for the first thread of the "process".
// fprintf(stderr, "Unlocking pid %d, storing thread %x current value: %x\n", current_pid, thread, thread_ids[current_pid]);
if (thread_ids[current_pid] == -1) {
thread_ids[current_pid] = thread;
}
pthread_mutex_unlock(&pid_mtx);
}
char* libc_getenv(const char* variableName) {
if (environment[current_pid] != NULL) {
if (variableName == NULL) { return NULL; }
// fprintf(stderr, "libc_getenv: %s\n", variableName); fflush(stderr);
char** envp = environment[current_pid];
unsigned long varNameLen = strlen(variableName);
if (varNameLen == 0) { return NULL; }
for (int i = 0; i < numVariablesSet[current_pid]; i++) {
if (envp[i] == NULL) { continue; }
if (strlen(envp[i]) < varNameLen) { continue; }
if (strncmp(variableName, envp[i], varNameLen) == 0) {
if (strlen(envp[i]) > varNameLen) {
if (envp[i][varNameLen] == '=') {
return envp[i] + varNameLen + 1;
}
}
}
/*
char* position = strchr(envp[i],'=');
if (strncmp(variableName, envp[i], position - envp[i]) == 0) {
char* value = position + 1;
return value;
}
*/
}
return NULL;
} else {
return getenv(variableName);
}
}
extern void set_session_errno(int n);
int ios_setenv_pid(const char* variableName, const char* value, int overwrite, int pid) {
if (environment[pid] != NULL) {
if (variableName == NULL) {
set_session_errno(EINVAL);
return -1;
}
if (strlen(variableName) == 0) {
set_session_errno(EINVAL);
return -1;
}
char* position = strchr(variableName,'=');
if (position != NULL) {
set_session_errno(EINVAL);
return -1;
}
char** envp = environment[pid];
unsigned long varNameLen = strlen(variableName);
for (int i = 0; i < numVariablesSet[pid]; i++) {
if (envp[i] == NULL) { continue; }
if (strncmp(variableName, envp[i], varNameLen) == 0) {
if (strlen(envp[i]) > varNameLen) {
if (envp[i][varNameLen] == '=') {
// This variable is defined in the current environment:
if (overwrite == 0) { return 0; }
envp[i] = realloc(envp[i], strlen(variableName) + strlen(value) + 2);
sprintf(envp[i], "%s=%s", variableName, value);
return 0;
}
}
}
}
// Not found so far, add it to the list:
int pos = numVariablesSet[pid];
environment[pid] = realloc(envp, (numVariablesSet[pid] + 2) * sizeof(char*));
environment[pid][pos] = malloc(strlen(variableName) + strlen(value) + 2);
environment[pid][pos + 1] = NULL;
sprintf(environment[pid][pos], "%s=%s", variableName, value);
numVariablesSet[pid] += 1;
return 0;
} else {
return setenv(variableName, value, overwrite);
}
}
int ios_setenv_parent(const char* variableName, const char* value, int overwrite) {
return ios_setenv_pid(variableName, value, overwrite, previousPid[current_pid]);
}
int ios_setenv(const char* variableName, const char* value, int overwrite) {
return ios_setenv_pid(variableName, value, overwrite, current_pid);
}
int ios_putenv(char* string) {
if (environment[current_pid] != NULL) {
unsigned length;
char *temp;
/* Find the length of the "NAME=" */
temp = strchr(string,'=');
if ( temp == 0 ) {
set_session_errno(EINVAL);
return( -1 );
}
length = (unsigned) (temp - string + 1);
/* Scan through the environment looking for "NAME=" */
char** envp = environment[current_pid];
for (int i = 0; i < numVariablesSet[current_pid]; i++) {
if (envp[i] == NULL) { continue; }
if ( strncmp( string, envp[i], length ) == 0 ) {
// Found it. Copy in place.
envp[i] = realloc(envp[i], strlen(string) + 1);
memcpy(envp[i], string, strlen(string) + 1);
return 0;
}
}
// Not found so far, add it to the list:
int pos = numVariablesSet[current_pid];
environment[current_pid] = realloc(envp, (numVariablesSet[current_pid] + 2) * sizeof(char*));
environment[current_pid][pos] = malloc(strlen(string) + 1);
environment[current_pid][pos + 1] = NULL;
memcpy(environment[current_pid][pos], string, strlen(string) + 1);
numVariablesSet[current_pid] += 1;
return 0;
} else {
return putenv(string);
}
}
int ios_unsetenv_pid(const char* variableName, int pid) {
// Someone calls unsetenv once the process has been terminated.
// Best thing to do is erase the environment and return
if (environment[pid] != NULL) {
if (variableName == NULL) {
set_session_errno(EINVAL);
return -1;
}
if (strlen(variableName) == 0) {
set_session_errno(EINVAL);
return -1;
}
char* position = strchr(variableName,'=');
if (position != NULL) {
set_session_errno(EINVAL);
return -1;
}
char** envp = environment[pid];
unsigned long varNameLen = strlen(variableName);
for (int i = 0; i < numVariablesSet[pid]; i++) {
if (envp[i] == NULL) { continue; }
if (strncmp(variableName, envp[i], varNameLen) == 0) {
if (strlen(envp[i]) > varNameLen) {
if (envp[i][varNameLen] == '=') {
// This variable is defined in the current environment:
free(envp[i]);
envp[i] = NULL;
if (i < numVariablesSet[pid] - 1) {
for (int j = i; j < numVariablesSet[pid] - 1; j++) {
envp[j] = envp[j+1];
}
envp[numVariablesSet[pid] - 1] = NULL;
}
numVariablesSet[pid] -= 1;
environment[pid] = realloc(envp, (numVariablesSet[pid] + 1) * sizeof(char*));
return 0;
}
}
}
}
/*
for (int i = 0; i < numVariablesSet[pid]; i++) {
char* position = strstr(envp[i],"=");
if (strncmp(variableName, envp[i], position - envp[i]) == 0) {
}
} */
// Not found:
return 0;
} else {
return unsetenv(variableName);
}
}
int ios_unsetenv_parent(const char* variableName) {
return ios_unsetenv_pid(variableName, previousPid[current_pid]);
}
int ios_unsetenv(const char* variableName) {
return ios_unsetenv_pid(variableName, current_pid);
}
// store environment variables (called from execve)
// Copy the entire environment:
extern char** environ;
void resetEnvironment(pid_t pid);
void storeEnvironment(char* envp[]) {
if (environment[current_pid] != NULL) {
// We already allocated one environment. Let's clean it:
resetEnvironment(current_pid);
}
int i = 0;
while (envp[i] != NULL) {
i++;
}
numVariablesSet[current_pid] = i;
environment[current_pid] = malloc((numVariablesSet[current_pid] + 1) * sizeof(char*));
for (int i = 0; i < numVariablesSet[current_pid]; i++) {
if (envp[i] != NULL)
environment[current_pid][i] = strdup(envp[i]);
else
environment[current_pid][i] = NULL;
}
// Keep NULL-termination:
environment[current_pid][numVariablesSet[current_pid]] = NULL;
}
// when the command is terminated, release the environment variables that were added.
void resetEnvironment(pid_t pid) {
if (environment[pid] != NULL) {
// Free the variables allocated:
for (int i = 0; i < numVariablesSet[pid]; i++) {
if (environment[pid][i] == NULL) { continue; }
free(environment[pid][i]);
environment[pid][i] = NULL;
}
free(environment[pid]);
environment[pid] = NULL;
numVariablesSet[pid] = 0;
}
}
// Used by "env -i": clear all environment variables, but don't clear the environment itself
void clearEnvironment(pid_t pid) {
if (environment[pid] != NULL) {
// Free the variables allocated:
for (int i = 0; i < numVariablesSet[pid]; i++) {
if (environment[pid][i] == NULL) { continue; }
free(environment[pid][i]);
environment[pid][i] = NULL;
}
numVariablesSet[pid] = 0;
}
}
char** environmentVariables(pid_t pid) {
if (environment[pid] != NULL) {
return environment[pid];
} else {
return environ;
}
}
extern int chdir_nolock(const char* path); // defined in ios_system.m
void ios_releaseThread(pthread_t thread) {
if (thread == NULL) {
return;
}
// TODO: this is inefficient. Replace with NSMutableArray?
for (int p = 0; p < IOS_MAX_THREADS; p++) {
if (thread_ids[p] == thread) {
// fprintf(stderr, "Found Id %d\n", p);
// Don't reset the environment; sometimes, commands try to change the environment while it is being erased.
// resetEnvironment(p);
// fprintf(stderr, "Reset current directory to %s because process %d terminates\n", previousDirectory[p], p);
current_pid = previousPid[p];
thread_ids[p] = NULL;
chdir_nolock(previousDirectory[p]);
return;
}
}
// fprintf(stderr, "Not found\n");
}
void ios_releaseBackgroundThread(pthread_t thread) {
// Same as ios_releaseThread, but do not reset the directory.
for (int p = 0; p < IOS_MAX_THREADS; p++) {
if (thread_ids[p] == thread) {
// fprintf(stderr, "Found Id %d\n", p);
current_pid = previousPid[p];
thread_ids[p] = NULL;
return;
}
}
// fprintf(stderr, "Not found\n");
}
void ios_releaseThreadId(pid_t pid) {
// Don't reset the environment; sometimes, commands try to change the environment while it is being erased.
// resetEnvironment(pid);
if (thread_ids[pid] != 0) {
// fprintf(stderr, "Locking for pid %d in ios_releaseThreadId\n", pid);
// fprintf(stderr, "Reset current directory to %s because process %d terminates\n", previousDirectory[pid], pid);
chdir_nolock(previousDirectory[pid]);
current_pid = previousPid[pid];
thread_ids[pid] = 0;
// fprintf(stderr, "Unlocking for pid %d in ios_releaseThreadId\n", pid);
} else {
// fprintf(stderr, "ios_releaseThreadId: pid %d was already terminated.\n", pid);
}
}
pid_t ios_currentPid(void) {
return current_pid;
}
// Note to self: do not redefine getpid() unless you have a way to make it consistent even when a "process" starts a new thread.
// 0MQ and asyncio rely on this.
pid_t fork(void) { return ios_nextAvailablePid(); } // increases current_pid by 1.
pid_t ios_fork(void) { return ios_nextAvailablePid(); } // increases current_pid by 1.
pid_t vfork(void) { return ios_nextAvailablePid(); }
// simple replacement of waitpid for swift programs
// We use "optnone" to prevent optimization, otherwise the while loops never end.
__attribute__ ((optnone)) void ios_waitpid(pid_t pid) {
executeWebAssemblyCommandsInOrder();
pthread_t threadToWaitFor;
// Old system: no explicit pid, just store last thread Id.
if ((pid == -1) || (pid == 0)) {
threadToWaitFor = ios_getLastThreadId();
while (threadToWaitFor != 0) {
threadToWaitFor = ios_getLastThreadId();
}
return;
}
// New system: thread Id is store with pid:
threadToWaitFor = ios_getThreadId(pid);
while (threadToWaitFor != 0) {
// -1: not started, >0 started, not finished, 0: finished
threadToWaitFor = ios_getThreadId(pid);
}
// fprintf(stderr, "Returning from ios_waitpid for %d \n", pid);
return;
}
__attribute__ ((optnone)) pid_t waitpid(pid_t pid, int *stat_loc, int options) {
// pthread_join won't work, because the thread might have been detached.
// (and you can't re-join a detached thread).
// -1 = the call waits for any child process (not good yet)
// 0 = the call waits for any child process in the process group of the caller
if (options && WNOHANG) {
executeWebAssemblyCommandsInOrder(); // start executing webAssembly commands
// WNOHANG: just check that the process is still running:
pthread_t threadToWaitFor;
if ((pid == -1) || (pid == 0)) threadToWaitFor = ios_getLastThreadId();
else threadToWaitFor = ios_getThreadId(pid);
if (threadToWaitFor != 0) // the process is still running
return 0;
else {
if (stat_loc) *stat_loc = W_EXITCODE(ios_getCommandStatus(), 0);
fflush(thread_stdout);
fflush(thread_stderr);
return pid; // was "-1". See man page and https://github.com/holzschu/ios_system/issues/89
}
} else {
// Wait until the process is terminated:
ios_waitpid(pid);
if (stat_loc) *stat_loc = W_EXITCODE(ios_getCommandStatus(), 0);
return pid;
}
}
//
void vwarn(const char *fmt, va_list args)
{
if (thread_stderr == NULL) thread_stderr = stderr;
fputs(ios_progname(), thread_stderr);
if (fmt != NULL)
{
fputs(": ", thread_stderr);
vfprintf(thread_stderr, fmt, args);
}
fputs(": ", thread_stderr);
fputs(strerror(errno), thread_stderr);
putc('\n', thread_stderr);
}
void vwarnx(const char *fmt, va_list args)
{
if (thread_stderr == NULL) thread_stderr = stderr;
fputs(ios_progname(), thread_stderr);
fputs(": ", thread_stderr);
if (fmt != NULL)
vfprintf(thread_stderr, fmt, args);
putc('\n', thread_stderr);
}
// void err(int eval, const char *fmt, ...);
void err(int eval, const char *fmt, ...) {
if (fmt != NULL) {
va_list argptr;
va_start(argptr, fmt);
vwarn(fmt, argptr);
va_end(argptr);
}
ios_exit(eval);
}
// void errc(int eval, int errorcode, const char *fmt, ...);
void errc(int eval, int errorcode, const char *fmt, ...) {
if (thread_stderr == NULL) thread_stderr = stderr;
if (fmt != NULL) {
va_list argptr;
va_start(argptr, fmt);
fputs(ios_progname(), thread_stderr);
fputs(": ", thread_stderr);
vfprintf(thread_stderr, fmt, argptr);
fputs(": ", thread_stderr);
fputs(strerror(errorcode), thread_stderr);
putc('\n', thread_stderr);
va_end(argptr);
}
ios_exit(eval);
}
// void errx(int eval, const char *fmt, ...);
void errx(int eval, const char *fmt, ...) {
if (fmt != NULL) {
va_list argptr;
va_start(argptr, fmt);
vwarnx(fmt, argptr);
va_end(argptr);
}
ios_exit(eval);
}
// void warn(const char *fmt, ...);
void warn(const char *fmt, ...) {
if (fmt != NULL) {
va_list argptr;
va_start(argptr, fmt);
vwarn(fmt, argptr);
va_end(argptr);
}
}
// void warnx(const char *fmt, ...);
void warnx(const char *fmt, ...) {
if (fmt != NULL) {
va_list argptr;
va_start(argptr, fmt);
vwarnx(fmt, argptr);
va_end(argptr);
}
}
// void warnc(int code, const char *fmt, ...);
void warnc(int code, const char *fmt, ...) {
if (thread_stderr == NULL) thread_stderr = stderr;
fputs(ios_progname(), thread_stderr);
if (fmt != NULL)
{
va_list argptr;
va_start(argptr, fmt);
fputs(": ", thread_stderr);
vfprintf(thread_stderr, fmt, argptr);
vwarn(fmt, argptr);
va_end(argptr);
}
fputs(": ", thread_stderr);
fputs(strerror(code), thread_stderr);
putc('\n', thread_stderr);
}