/home/uke/oil/cpp/core.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // core.cc |
2 | | |
3 | | #include "cpp/core.h" |
4 | | |
5 | | #include <ctype.h> // ispunct() |
6 | | #include <errno.h> |
7 | | #include <math.h> // fmod() |
8 | | #include <pwd.h> // passwd |
9 | | #include <signal.h> |
10 | | #include <sys/resource.h> // getrusage |
11 | | #include <sys/select.h> // select(), FD_ISSET, FD_SET, FD_ZERO |
12 | | #include <sys/stat.h> // stat |
13 | | #include <sys/time.h> // gettimeofday |
14 | | #include <sys/times.h> // tms / times() |
15 | | #include <sys/utsname.h> // uname |
16 | | #include <sys/wait.h> // waitpid() |
17 | | #include <termios.h> // tcgetattr(), tcsetattr() |
18 | | #include <time.h> // time() |
19 | | #include <unistd.h> // getuid(), environ |
20 | | |
21 | | #include "_build/detected-cpp-config.h" // HAVE_PWENT |
22 | | #include "_gen/cpp/build_stamp.h" // gCommitHash |
23 | | #include "_gen/frontend/consts.h" // gVersion |
24 | | #include "cpp/embedded_file.h" |
25 | | |
26 | | extern char** environ; |
27 | | |
28 | | namespace pyos { |
29 | | |
30 | | SignalSafe* gSignalSafe = nullptr; |
31 | | |
32 | 1 | Tuple2<int, int> WaitPid(int waitpid_options) { |
33 | 1 | int status; |
34 | 1 | int result = ::waitpid(-1, &status, WUNTRACED | waitpid_options); |
35 | 1 | if (result < 0) { |
36 | 1 | if (errno == EINTR && gSignalSafe->PollSigInt()) { |
37 | 0 | throw Alloc<KeyboardInterrupt>(); |
38 | 0 | } |
39 | 1 | return Tuple2<int, int>(-1, errno); |
40 | 1 | } |
41 | 0 | return Tuple2<int, int>(result, status); |
42 | 1 | } |
43 | | |
44 | 2 | Tuple2<int, int> Read(int fd, int n, List<BigStr*>* chunks) { |
45 | 2 | BigStr* s = OverAllocatedStr(n); // Allocate enough for the result |
46 | | |
47 | 2 | int length = ::read(fd, s->data(), n); |
48 | 2 | if (length < 0) { |
49 | 0 | if (errno == EINTR && gSignalSafe->PollSigInt()) { |
50 | 0 | throw Alloc<KeyboardInterrupt>(); |
51 | 0 | } |
52 | 0 | return Tuple2<int, int>(-1, errno); |
53 | 0 | } |
54 | 2 | if (length == 0) { |
55 | 1 | return Tuple2<int, int>(length, 0); |
56 | 1 | } |
57 | | |
58 | | // Now we know how much data we got back |
59 | 1 | s->MaybeShrink(length); |
60 | 1 | chunks->append(s); |
61 | | |
62 | 1 | return Tuple2<int, int>(length, 0); |
63 | 2 | } |
64 | | |
65 | 3 | Tuple2<int, int> ReadByte(int fd) { |
66 | 3 | unsigned char buf[1]; |
67 | 3 | ssize_t n = read(fd, &buf, 1); |
68 | 3 | if (n < 0) { // read error |
69 | 0 | if (errno == EINTR && gSignalSafe->PollSigInt()) { |
70 | 0 | throw Alloc<KeyboardInterrupt>(); |
71 | 0 | } |
72 | 0 | return Tuple2<int, int>(-1, errno); |
73 | 3 | } else if (n == 0) { // EOF |
74 | 1 | return Tuple2<int, int>(EOF_SENTINEL, 0); |
75 | 2 | } else { // return character |
76 | 2 | return Tuple2<int, int>(buf[0], 0); |
77 | 2 | } |
78 | 3 | } |
79 | | |
80 | 1 | Dict<BigStr*, BigStr*>* Environ() { |
81 | 1 | auto d = Alloc<Dict<BigStr*, BigStr*>>(); |
82 | | |
83 | 12 | for (char** env = environ; *env; ++env) { |
84 | 11 | char* pair = *env; |
85 | | |
86 | 11 | char* eq = strchr(pair, '='); |
87 | 11 | assert(eq != nullptr); // must look like KEY=value |
88 | | |
89 | 0 | int len = strlen(pair); |
90 | | |
91 | 11 | int key_len = eq - pair; |
92 | 11 | BigStr* key = StrFromC(pair, key_len); |
93 | | |
94 | 11 | int val_len = len - key_len - 1; |
95 | 11 | BigStr* val = StrFromC(eq + 1, val_len); |
96 | | |
97 | 11 | d->set(key, val); |
98 | 11 | } |
99 | | |
100 | 1 | return d; |
101 | 1 | } |
102 | | |
103 | 3 | int Chdir(BigStr* dest_dir) { |
104 | 3 | if (chdir(dest_dir->data_) == 0) { |
105 | 2 | return 0; // success |
106 | 2 | } else { |
107 | 1 | return errno; |
108 | 1 | } |
109 | 3 | } |
110 | | |
111 | 1 | BigStr* GetMyHomeDir() { |
112 | 1 | uid_t uid = getuid(); // always succeeds |
113 | | |
114 | | // Don't free this. (May return a pointer to a static area) |
115 | 1 | struct passwd* entry = getpwuid(uid); |
116 | 1 | if (entry == nullptr) { |
117 | 0 | return nullptr; |
118 | 0 | } |
119 | 1 | BigStr* s = StrFromC(entry->pw_dir); |
120 | 1 | return s; |
121 | 1 | } |
122 | | |
123 | 1 | BigStr* GetHomeDir(BigStr* user_name) { |
124 | | // Don't free this. (May return a pointer to a static area) |
125 | 1 | struct passwd* entry = getpwnam(user_name->data_); |
126 | 1 | if (entry == nullptr) { |
127 | 0 | return nullptr; |
128 | 0 | } |
129 | 1 | BigStr* s = StrFromC(entry->pw_dir); |
130 | 1 | return s; |
131 | 1 | } |
132 | | |
133 | 1 | List<PasswdEntry*>* GetAllUsers() { |
134 | 1 | #ifdef HAVE_PWENT |
135 | 1 | auto* ret = NewList<PasswdEntry*>(); |
136 | 1 | struct passwd* entry = nullptr; |
137 | | |
138 | 1 | setpwent(); |
139 | 21 | while (true) { |
140 | 21 | errno = 0; |
141 | 21 | entry = getpwent(); |
142 | 21 | if (entry == nullptr) { |
143 | 1 | if (errno == EINTR) { |
144 | 0 | continue; // try again |
145 | 1 | } else if (errno != 0) { |
146 | 0 | throw Alloc<OSError>(errno); |
147 | 0 | } |
148 | 1 | break; |
149 | 1 | } |
150 | 20 | ret->append(Alloc<PasswdEntry>(entry)); |
151 | 20 | } |
152 | 1 | endpwent(); |
153 | | |
154 | 1 | return ret; |
155 | | #else |
156 | | fprintf( |
157 | | stderr, |
158 | | "Oils compiled without libc *pwent() functions. Can't list users.\n"); |
159 | | return NewList<PasswdEntry*>(); |
160 | | #endif |
161 | 1 | } |
162 | | |
163 | 2 | BigStr* GetUserName(int uid) { |
164 | 2 | BigStr* result = kEmptyString; |
165 | | |
166 | 2 | if (passwd* pw = getpwuid(uid)) { |
167 | 2 | result = StrFromC(pw->pw_name); |
168 | 2 | } else { |
169 | 0 | throw Alloc<IOError>(errno); |
170 | 0 | } |
171 | | |
172 | 2 | return result; |
173 | 2 | } |
174 | | |
175 | 1 | BigStr* OsType() { |
176 | 1 | BigStr* result = kEmptyString; |
177 | | |
178 | 1 | utsname un = {}; |
179 | 1 | if (::uname(&un) == 0) { |
180 | 1 | result = StrFromC(un.sysname); |
181 | 1 | } else { |
182 | 0 | throw Alloc<IOError>(errno); |
183 | 0 | } |
184 | | |
185 | 1 | return result; |
186 | 1 | } |
187 | | |
188 | 0 | Tuple2<mops::BigInt, mops::BigInt> GetRLimit(int resource) { |
189 | 0 | struct rlimit lim; |
190 | 0 | if (::getrlimit(resource, &lim) < 0) { |
191 | 0 | throw Alloc<IOError>(errno); |
192 | 0 | } |
193 | 0 | return Tuple2<mops::BigInt, mops::BigInt>(lim.rlim_cur, lim.rlim_max); |
194 | 0 | } |
195 | | |
196 | 0 | void SetRLimit(int resource, mops::BigInt soft, mops::BigInt hard) { |
197 | 0 | struct rlimit lim; |
198 | 0 | lim.rlim_cur = soft; |
199 | 0 | lim.rlim_max = hard; |
200 | |
|
201 | 0 | if (::setrlimit(resource, &lim) < 0) { |
202 | 0 | throw Alloc<IOError>(errno); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | 1 | Tuple3<double, double, double> Time() { |
207 | 1 | struct timeval now; |
208 | 1 | if (gettimeofday(&now, nullptr) < 0) { |
209 | 0 | throw Alloc<IOError>(errno); // could be a permission error |
210 | 0 | } |
211 | 1 | double real = now.tv_sec + static_cast<double>(now.tv_usec) / 1e6; |
212 | | |
213 | 1 | struct rusage ru; |
214 | 1 | if (::getrusage(RUSAGE_SELF, &ru) == -1) { |
215 | 0 | throw Alloc<IOError>(errno); |
216 | 0 | } |
217 | 1 | struct timeval* u = &(ru.ru_utime); |
218 | 1 | struct timeval* s = &(ru.ru_stime); |
219 | | |
220 | 1 | double user = u->tv_sec + static_cast<double>(u->tv_usec) / 1e6; |
221 | 1 | double sys = s->tv_sec + static_cast<double>(s->tv_usec) / 1e6; |
222 | | |
223 | 1 | return Tuple3<double, double, double>(real, user, sys); |
224 | 1 | } |
225 | | |
226 | 4 | static void PrintClock(clock_t ticks, long ticks_per_sec) { |
227 | 4 | double seconds = static_cast<double>(ticks) / ticks_per_sec; |
228 | 4 | printf("%ldm%.3fs", static_cast<long>(seconds) / 60, fmod(seconds, 60)); |
229 | 4 | } |
230 | | |
231 | | // bash source: builtins/times.def |
232 | 1 | void PrintTimes() { |
233 | 1 | struct tms t; |
234 | 1 | if (times(&t) == -1) { |
235 | 0 | throw Alloc<IOError>(errno); |
236 | 0 | } |
237 | 1 | long ticks_per_sec = sysconf(_SC_CLK_TCK); |
238 | | |
239 | 1 | PrintClock(t.tms_utime, ticks_per_sec); |
240 | 1 | putc(' ', stdout); |
241 | 1 | PrintClock(t.tms_stime, ticks_per_sec); |
242 | 1 | putc('\n', stdout); |
243 | 1 | PrintClock(t.tms_cutime, ticks_per_sec); |
244 | 1 | putc(' ', stdout); |
245 | 1 | PrintClock(t.tms_cstime, ticks_per_sec); |
246 | 1 | putc('\n', stdout); |
247 | 1 | } |
248 | | |
249 | 0 | bool InputAvailable(int fd) { |
250 | 0 | fd_set fds; |
251 | 0 | FD_ZERO(&fds); |
252 | 0 | struct timeval timeout = {0}; // return immediately |
253 | 0 | FD_SET(fd, &fds); |
254 | 0 | return select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0; |
255 | 0 | } |
256 | | |
257 | 1 | IOError_OSError* FlushStdout() { |
258 | | // Flush libc buffers |
259 | 1 | if (::fflush(stdout) != 0) { |
260 | 0 | return Alloc<IOError>(errno); |
261 | 0 | } |
262 | 1 | return nullptr; |
263 | 1 | } |
264 | | |
265 | 1 | SignalSafe* InitSignalSafe() { |
266 | 1 | gSignalSafe = Alloc<SignalSafe>(); |
267 | 1 | gHeap.RootGlobalVar(gSignalSafe); |
268 | | |
269 | 1 | RegisterSignalInterest(SIGINT); // for KeyboardInterrupt checks |
270 | | |
271 | 1 | return gSignalSafe; |
272 | 1 | } |
273 | | |
274 | 2 | void Sigaction(int sig_num, void (*handler)(int)) { |
275 | 2 | struct sigaction act = {}; |
276 | 2 | act.sa_handler = handler; |
277 | 2 | if (sigaction(sig_num, &act, nullptr) != 0) { |
278 | 0 | throw Alloc<OSError>(errno); |
279 | 0 | } |
280 | 2 | } |
281 | | |
282 | 4 | static void signal_handler(int sig_num) { |
283 | 4 | assert(gSignalSafe != nullptr); |
284 | 0 | gSignalSafe->UpdateFromSignalHandler(sig_num); |
285 | 4 | } |
286 | | |
287 | 4 | void RegisterSignalInterest(int sig_num) { |
288 | 4 | struct sigaction act = {}; |
289 | 4 | act.sa_handler = signal_handler; |
290 | 4 | assert(sigaction(sig_num, &act, nullptr) == 0); |
291 | 4 | } |
292 | | |
293 | 2 | Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path) { |
294 | 2 | struct stat st; |
295 | 2 | if (::stat(path->data(), &st) == -1) { |
296 | 1 | throw Alloc<OSError>(errno); |
297 | 1 | } |
298 | | |
299 | 1 | return Alloc<Tuple2<BigStr*, int>>(path, st.st_mtime); |
300 | 2 | } |
301 | | |
302 | 0 | Tuple2<int, void*> PushTermAttrs(int fd, int mask) { |
303 | 0 | struct termios* term_attrs = |
304 | 0 | static_cast<struct termios*>(malloc(sizeof(struct termios))); |
305 | |
|
306 | 0 | if (tcgetattr(fd, term_attrs) < 0) { |
307 | 0 | throw Alloc<OSError>(errno); |
308 | 0 | } |
309 | | // Flip the bits in one field |
310 | 0 | int orig_local_modes = term_attrs->c_lflag; |
311 | 0 | term_attrs->c_lflag = orig_local_modes & mask; |
312 | |
|
313 | 0 | if (tcsetattr(fd, TCSANOW, term_attrs) < 0) { |
314 | 0 | throw Alloc<OSError>(errno); |
315 | 0 | } |
316 | | |
317 | 0 | return Tuple2<int, void*>(orig_local_modes, term_attrs); |
318 | 0 | } |
319 | | |
320 | 0 | void PopTermAttrs(int fd, int orig_local_modes, void* term_attrs) { |
321 | 0 | struct termios* t = static_cast<struct termios*>(term_attrs); |
322 | 0 | t->c_lflag = orig_local_modes; |
323 | 0 | if (tcsetattr(fd, TCSANOW, t) < 0) { |
324 | 0 | ; // Like Python, ignore error because of issue #1001 |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | } // namespace pyos |
329 | | |
330 | | namespace pyutil { |
331 | | |
332 | | static grammar::Grammar* gOilGrammar = nullptr; |
333 | | |
334 | | // TODO: SHARE with pyext |
335 | 2 | bool IsValidCharEscape(BigStr* c) { |
336 | 2 | DCHECK(len(c) == 1); |
337 | | |
338 | 0 | int ch = c->data_[0]; |
339 | | |
340 | 2 | if (ch == '/' || ch == '.' || ch == '-') { |
341 | 0 | return false; |
342 | 0 | } |
343 | 2 | if (ch == ' ') { // foo\ bar is idiomatic |
344 | 0 | return true; |
345 | 0 | } |
346 | 2 | return ispunct(ch); |
347 | 2 | } |
348 | | |
349 | 3 | BigStr* ChArrayToString(List<int>* ch_array) { |
350 | 3 | int n = len(ch_array); |
351 | 3 | BigStr* result = NewStr(n); |
352 | 11 | for (int i = 0; i < n; ++i) { |
353 | 8 | result->data_[i] = ch_array->at(i); |
354 | 8 | } |
355 | 3 | result->data_[n] = '\0'; |
356 | 3 | return result; |
357 | 3 | } |
358 | | |
359 | 3 | BigStr* _ResourceLoader::Get(BigStr* path) { |
360 | 3 | TextFile* t = gEmbeddedFiles; // start of generated data |
361 | 6 | while (t->rel_path != nullptr) { |
362 | 5 | if (strcmp(t->rel_path, path->data_) == 0) { |
363 | 2 | return t->contents; |
364 | 2 | } |
365 | 3 | t++; |
366 | 3 | } |
367 | | // Emulate Python |
368 | 1 | throw Alloc<IOError>(ENOENT); |
369 | 3 | } |
370 | | |
371 | 1 | _ResourceLoader* GetResourceLoader() { |
372 | 1 | return Alloc<_ResourceLoader>(); |
373 | 1 | } |
374 | | |
375 | 1 | BigStr* GetVersion(_ResourceLoader* loader) { |
376 | 1 | return consts::gVersion; |
377 | 1 | } |
378 | | |
379 | 1 | void PrintVersionDetails(_ResourceLoader* loader) { |
380 | | // Invoked by core/util.py VersionFlag() |
381 | 1 | printf("git commit = %s\n", gCommitHash); |
382 | | |
383 | | // TODO: I would like the CPU, OS, compiler |
384 | | // How do we get those? Look at CPython |
385 | 1 | } |
386 | | |
387 | 2 | BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars) { |
388 | 2 | int upper_bound = len(s) * 2; |
389 | 2 | BigStr* buf = OverAllocatedStr(upper_bound); |
390 | 2 | char* p = buf->data_; |
391 | | |
392 | 11 | for (int i = 0; i < len(s); ++i) { |
393 | 9 | char c = s->data_[i]; |
394 | 9 | if (memchr(meta_chars->data_, c, len(meta_chars))) { |
395 | 3 | *p++ = '\\'; |
396 | 3 | } |
397 | 9 | *p++ = c; |
398 | 9 | } |
399 | 2 | buf->MaybeShrink(p - buf->data_); |
400 | 2 | return buf; |
401 | 2 | } |
402 | | |
403 | 1 | BigStr* strerror(IOError_OSError* e) { |
404 | 1 | BigStr* s = StrFromC(::strerror(e->errno_)); |
405 | 1 | return s; |
406 | 1 | } |
407 | | |
408 | 0 | grammar::Grammar* LoadYshGrammar(_ResourceLoader*) { |
409 | 0 | if (gOilGrammar != nullptr) { |
410 | 0 | return gOilGrammar; |
411 | 0 | } |
412 | | |
413 | 0 | gOilGrammar = Alloc<grammar::Grammar>(); |
414 | 0 | gHeap.RootGlobalVar(gOilGrammar); |
415 | 0 | return gOilGrammar; |
416 | 0 | } |
417 | | |
418 | | } // namespace pyutil |