aboutsummaryrefslogtreecommitdiff
path: root/lib/portability.c
blob: 80ca8c9afec1b3e444791f0ac572f66b6e781424 (plain)
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
/* portability.c - code to workaround the deficiencies of various platforms.
 *
 * Copyright 2012 Rob Landley <rob@landley.net>
 * Copyright 2012 Georgi Chorbadzhiyski <gf@unixsol.org>
 */

#include "toys.h"

// We can't fork() on nommu systems, and vfork() requires an exec() or exit()
// before resuming the parent (because they share a heap until then). And no,
// we can't implement our own clone() call that does the equivalent of fork()
// because nommu heaps use physical addresses so if we copy the heap all our
// pointers are wrong. (You need an mmu in order to map two heaps to the same
// address range without interfering with each other.) In the absence of
// a portable way to tell malloc() to start a new heap without freeing the old
// one, you pretty much need the exec().)

// So we exec ourselves (via /proc/self/exe, if anybody knows a way to
// re-exec self without depending on the filesystem, I'm all ears),
// and use the arguments to signal reentry.

#if CFG_TOYBOX_FORK
pid_t xfork(void)
{
  pid_t pid = fork();

  if (pid < 0) perror_exit("fork");

  return pid;
}
#endif

void xgetrandom(void *buf, unsigned buflen, unsigned flags)
{
  int fd;

#if CFG_TOYBOX_GETRANDOM
  if (buflen != getrandom(buf, buflen, flags))
    if (!CFG_TOYBOX_ANDROID || errno!=ENOSYS) perror_exit("getrandom");
#endif

  fd = xopen(flags ? "/dev/random" : "/dev/urandom", O_RDONLY);
  xreadall(fd, buf, buflen);
  close(fd);
}

#if defined(__APPLE__)
ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream)
{
  int ch;
  size_t new_len;
  ssize_t i = 0;
  char *line, *new_line;

  // Invalid input
  if (!linep || !np) {
    errno = EINVAL;
    return -1;
  }

  if (*linep == NULL || *np == 0) {
    *np = 1024;
    *linep = calloc(1, *np);
    if (*linep == NULL) return -1;
  }
  line = *linep;

  while ((ch = getc(stream)) != EOF) {
    if (i > *np) {
      // Need more space
      new_len = *np + 1024;
      new_line = realloc(*linep, new_len);
      if (!new_line) return -1;
      *np = new_len;
      line = *linep = new_line;
    }

    line[i++] = ch;
    if (ch == delim) break;
  }

  if (i > *np) {
    // Need more space
    new_len  = i + 2;
    new_line = realloc(*linep, new_len);
    if (!new_line) return -1;
    *np = new_len;
    line = *linep = new_line;
  }
  line[i] = '\0';

  return i > 0 ? i : -1;
}

ssize_t getline(char **linep, size_t *np, FILE *stream)
{
  return getdelim(linep, np, '\n', stream);
}

extern char **environ;

int clearenv(void)
{
  *environ = NULL;
  return 0;
}
#endif