/home/uke/oil/cpp/leaky_stdlib.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // leaky_stdlib.cc: Replacement for standard library modules |
2 | | // and native/posixmodule.c |
3 | | |
4 | | // clang-format off |
5 | | #include "mycpp/myerror.h" // for OSError; must come first |
6 | | // clang-format on |
7 | | |
8 | | #include "leaky_stdlib.h" |
9 | | |
10 | | #include <errno.h> |
11 | | #include <fcntl.h> // open |
12 | | #include <sys/stat.h> // umask |
13 | | #include <sys/types.h> // umask |
14 | | #include <sys/wait.h> // WUNTRACED |
15 | | #include <time.h> |
16 | | #include <unistd.h> |
17 | | |
18 | | #include "cpp/leaky_core_error.h" |
19 | | #include "cpp/leaky_core_pyerror.h" |
20 | | #include "mycpp/mylib_leaky.h" |
21 | | using mylib::CopyStr; |
22 | | using mylib::OverAllocatedStr; |
23 | | |
24 | | namespace fcntl_ { |
25 | | |
26 | 0 | int fcntl(int fd, int cmd) { |
27 | 0 | int result = ::fcntl(fd, cmd); |
28 | 0 | if (result < 0) { |
29 | 0 | throw new IOError(errno); |
30 | 0 | } |
31 | 0 | return result; |
32 | 0 | } |
33 | | |
34 | 0 | int fcntl(int fd, int cmd, int arg) { |
35 | 0 | int result = ::fcntl(fd, cmd, arg); |
36 | 0 | if (result < 0) { |
37 | 0 | throw new IOError(errno); |
38 | 0 | } |
39 | 0 | return result; |
40 | 0 | } |
41 | | |
42 | | } // namespace fcntl_ |
43 | | |
44 | | namespace posix { |
45 | | |
46 | 0 | int umask(int mask) { |
47 | | // note: assuming mode_t fits in an int |
48 | 0 | return ::umask(mask); |
49 | 0 | } |
50 | | |
51 | 0 | int open(Str* path, int flags, int perms) { |
52 | 0 | mylib::Str0 path0(path); |
53 | 0 | return ::open(path0.Get(), flags, perms); |
54 | 0 | } |
55 | | |
56 | 0 | void dup2(int oldfd, int newfd) { |
57 | 0 | if (::dup2(oldfd, newfd) < 0) { |
58 | 0 | throw new OSError(errno); |
59 | 0 | } |
60 | 0 | } |
61 | 1 | void putenv(Str* name, Str* value) { |
62 | 1 | assert(name->IsNulTerminated()); |
63 | 0 | assert(value->IsNulTerminated()); |
64 | 0 | int overwrite = 1; |
65 | 1 | int ret = ::setenv(name->data_, value->data_, overwrite); |
66 | 1 | if (ret < 0) { |
67 | 0 | throw new IOError(errno); |
68 | 0 | } |
69 | 1 | } |
70 | | |
71 | 0 | mylib::LineReader* fdopen(int fd, Str* c_mode) { |
72 | 0 | mylib::Str0 c_mode0(c_mode); |
73 | 0 | FILE* f = ::fdopen(fd, c_mode0.Get()); |
74 | | |
75 | | // TODO: raise exception |
76 | 0 | assert(f); |
77 | | |
78 | 0 | return new mylib::CFileLineReader(f); |
79 | 0 | } |
80 | | |
81 | 0 | void execve(Str* argv0, List<Str*>* argv, Dict<Str*, Str*>* environ) { |
82 | 0 | mylib::Str0 _argv0(argv0); |
83 | |
|
84 | 0 | int n_args = len(argv); |
85 | | // never deallocated |
86 | 0 | char** _argv = static_cast<char**>(malloc((n_args + 1) * sizeof(char*))); |
87 | | |
88 | | // Annoying const_cast |
89 | | // https://stackoverflow.com/questions/190184/execv-and-const-ness |
90 | 0 | for (int i = 0; i < n_args; ++i) { |
91 | 0 | _argv[i] = const_cast<char*>(argv->index_(i)->data_); |
92 | 0 | } |
93 | 0 | _argv[n_args] = nullptr; |
94 | | |
95 | | // Convert environ into an array of pointers to strings of the form: "k=v". |
96 | 0 | int n_env = len(environ); |
97 | 0 | char** envp = static_cast<char**>(malloc((n_env + 1) * sizeof(char*))); |
98 | 0 | int i = 0; |
99 | 0 | for (const auto& kv : environ->items_) { |
100 | 0 | Str* k = kv.first; |
101 | 0 | Str* v = kv.second; |
102 | 0 | int joined_len = k->len_ + v->len_ + 1; |
103 | 0 | char* buf = static_cast<char*>(malloc(joined_len + 1)); |
104 | 0 | memcpy(buf, k->data_, k->len_); |
105 | 0 | buf[k->len_] = '='; |
106 | 0 | memcpy(buf + k->len_ + 1, v->data_, v->len_); |
107 | 0 | buf[joined_len] = '\0'; |
108 | 0 | envp[i++] = buf; |
109 | 0 | } |
110 | 0 | envp[n_env] = nullptr; |
111 | |
|
112 | 0 | int ret = ::execve(_argv0.Get(), _argv, envp); |
113 | 0 | if (ret == -1) { |
114 | 0 | throw new OSError(errno); |
115 | 0 | } |
116 | | |
117 | | // NOTE(Jesse): ::execve() is specified to never return on success. If we |
118 | | // hit this assertion, it returned successfully (or at least something other |
119 | | // than -1) but should have overwritten our address space with the invoked |
120 | | // process' |
121 | 0 | InvalidCodePath(); |
122 | 0 | } |
123 | | |
124 | | } // namespace posix |
125 | | |
126 | | namespace time_ { |
127 | | |
128 | 0 | void tzset() { |
129 | 0 | ::tzset(); |
130 | 0 | } |
131 | | |
132 | 1 | time_t time() { |
133 | 1 | return ::time(nullptr); |
134 | 1 | } |
135 | | |
136 | | // NOTE(Jesse): time_t is specified to be an arithmetic type by C++. On most |
137 | | // systems it's a 64-bit integer. 64 bits is used because 32 will overflow in |
138 | | // 2038. Someone on a comittee somewhere thought of that when moving to 64-bit |
139 | | // architectures to prevent breaking ABI again; on 32-bit systems it's usually |
140 | | // 32 bits. Point being, using anything but the time_t typedef here could |
141 | | // (unlikely, but possible) produce weird behavior. |
142 | 0 | time_t localtime(time_t ts) { |
143 | 0 | tm* loc_time = ::localtime(&ts); |
144 | 0 | time_t result = mktime(loc_time); |
145 | 0 | return result; |
146 | 0 | } |
147 | | |
148 | 0 | Str* strftime(Str* s, time_t ts) { |
149 | | // TODO: may not work with mylib_leaky.h |
150 | | // https://github.com/oilshell/oil/issues/1221 |
151 | 0 | assert(s->IsNulTerminated()); |
152 | | |
153 | 0 | tm* loc_time = ::localtime(&ts); |
154 | |
|
155 | 0 | const int max_len = 1024; |
156 | 0 | Str* result = OverAllocatedStr(max_len); |
157 | 0 | int n = strftime(result->data(), max_len, s->data_, loc_time); |
158 | 0 | if (n == 0) { |
159 | | // bash silently truncates on large format string like |
160 | | // printf '%(%Y)T' |
161 | | // Oil doesn't mask errors |
162 | | // No error location info, but leaving it out points reliably to 'printf' |
163 | 0 | e_die(CopyStr("strftime() result exceeds 1024 bytes")); |
164 | 0 | } |
165 | 0 | result->SetObjLenFromStrLen(n); |
166 | 0 | return result; |
167 | 0 | } |
168 | | |
169 | | } // namespace time_ |