Serenity Operating System
at hosted 710 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/LogStream.h> 28#include <AK/PrintfImplementation.h> 29#include <AK/ScopedValueRollback.h> 30#include <AK/StdLibExtras.h> 31#include <Kernel/Syscall.h> 32#include <assert.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <stdarg.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <sys/types.h> 40#include <sys/wait.h> 41#include <unistd.h> 42 43extern "C" { 44 45static FILE __default_streams[3]; 46FILE* stdin; 47FILE* stdout; 48FILE* stderr; 49 50void init_FILE(FILE& fp, int fd, int mode) 51{ 52 fp.fd = fd; 53 fp.buffer = fp.default_buffer; 54 fp.buffer_size = BUFSIZ; 55 fp.mode = mode; 56} 57 58static FILE* make_FILE(int fd) 59{ 60 auto* fp = (FILE*)malloc(sizeof(FILE)); 61 memset(fp, 0, sizeof(FILE)); 62 init_FILE(*fp, fd, isatty(fd)); 63 return fp; 64} 65 66void __stdio_init() 67{ 68 stdin = &__default_streams[0]; 69 stdout = &__default_streams[1]; 70 stderr = &__default_streams[2]; 71 init_FILE(*stdin, 0, isatty(0) ? _IOLBF : _IOFBF); 72 init_FILE(*stdout, 1, isatty(1) ? _IOLBF : _IOFBF); 73 init_FILE(*stderr, 2, _IONBF); 74} 75 76int setvbuf(FILE* stream, char* buf, int mode, size_t size) 77{ 78 if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) { 79 errno = EINVAL; 80 return -1; 81 } 82 stream->mode = mode; 83 if (buf) { 84 stream->buffer = buf; 85 stream->buffer_size = size; 86 } else { 87 stream->buffer = stream->default_buffer; 88 stream->buffer_size = BUFSIZ; 89 } 90 stream->buffer_index = 0; 91 return 0; 92} 93 94void setbuf(FILE* stream, char* buf) 95{ 96 setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); 97} 98 99void setlinebuf(FILE* stream) 100{ 101 setvbuf(stream, nullptr, _IOLBF, 0); 102} 103 104int fileno(FILE* stream) 105{ 106 assert(stream); 107 return stream->fd; 108} 109 110int feof(FILE* stream) 111{ 112 assert(stream); 113 return stream->eof; 114} 115 116int fflush(FILE* stream) 117{ 118 if (!stream) { 119 dbg() << "FIXME: fflush(nullptr) should flush all open streams"; 120 return 0; 121 } 122 if (!stream->buffer_index) 123 return 0; 124 int rc = write(stream->fd, stream->buffer, stream->buffer_index); 125 stream->buffer_index = 0; 126 stream->error = 0; 127 stream->eof = 0; 128 stream->have_ungotten = false; 129 stream->ungotten = 0; 130 if (rc < 0) { 131 stream->error = errno; 132 return EOF; 133 } 134 return 0; 135} 136 137char* fgets(char* buffer, int size, FILE* stream) 138{ 139 ASSERT(stream); 140 ASSERT(size); 141 ssize_t nread = 0; 142 while (nread < (size - 1)) { 143 int ch = fgetc(stream); 144 if (ch == EOF) 145 break; 146 buffer[nread++] = ch; 147 if (ch == '\n') 148 break; 149 } 150 if (nread) { 151 buffer[nread] = '\0'; 152 return buffer; 153 } 154 return nullptr; 155} 156 157int fgetc(FILE* stream) 158{ 159 assert(stream); 160 char ch; 161 size_t nread = fread(&ch, sizeof(char), 1, stream); 162 if (nread == 1) 163 return ch; 164 return EOF; 165} 166 167int getc(FILE* stream) 168{ 169 return fgetc(stream); 170} 171 172int getc_unlocked(FILE* stream) 173{ 174 return fgetc(stream); 175} 176 177int getchar() 178{ 179 return getc(stdin); 180} 181 182ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) 183{ 184 char *ptr, *eptr; 185 if (*lineptr == nullptr || *n == 0) { 186 *n = BUFSIZ; 187 if ((*lineptr = static_cast<char*>(malloc(*n))) == nullptr) { 188 return -1; 189 } 190 } 191 192 for (ptr = *lineptr, eptr = *lineptr + *n;;) { 193 int c = fgetc(stream); 194 if (c == -1) { 195 if (feof(stream)) { 196 *ptr = '\0'; 197 return ptr == *lineptr ? -1 : ptr - *lineptr; 198 } else { 199 return -1; 200 } 201 } 202 *ptr++ = c; 203 if (c == delim) { 204 *ptr = '\0'; 205 return ptr - *lineptr; 206 } 207 if (ptr + 2 >= eptr) { 208 char* nbuf; 209 size_t nbuf_sz = *n * 2; 210 ssize_t d = ptr - *lineptr; 211 if ((nbuf = static_cast<char*>(realloc(*lineptr, nbuf_sz))) == nullptr) { 212 return -1; 213 } 214 *lineptr = nbuf; 215 *n = nbuf_sz; 216 eptr = nbuf + nbuf_sz; 217 ptr = nbuf + d; 218 } 219 } 220} 221 222ssize_t getline(char** lineptr, size_t* n, FILE* stream) 223{ 224 return getdelim(lineptr, n, '\n', stream); 225} 226 227int ungetc(int c, FILE* stream) 228{ 229 ASSERT(stream); 230 if (c == EOF) 231 return EOF; 232 if (stream->have_ungotten) 233 return EOF; 234 stream->have_ungotten = true; 235 stream->ungotten = c; 236 stream->eof = false; 237 return c; 238} 239 240int fputc(int ch, FILE* stream) 241{ 242 assert(stream); 243 assert(stream->buffer_index < stream->buffer_size); 244 stream->buffer[stream->buffer_index++] = ch; 245 if (stream->buffer_index >= stream->buffer_size) 246 fflush(stream); 247 else if (stream->mode == _IONBF || (stream->mode == _IOLBF && ch == '\n')) 248 fflush(stream); 249 if (stream->eof || stream->error) 250 return EOF; 251 return (u8)ch; 252} 253 254int putc(int ch, FILE* stream) 255{ 256 return fputc(ch, stream); 257} 258 259int putchar(int ch) 260{ 261 return putc(ch, stdout); 262} 263 264int fputs(const char* s, FILE* stream) 265{ 266 for (; *s; ++s) { 267 int rc = putc(*s, stream); 268 if (rc == EOF) 269 return EOF; 270 } 271 return 1; 272} 273 274int puts(const char* s) 275{ 276 int rc = fputs(s, stdout); 277 if (rc == EOF) 278 return EOF; 279 return fputc('\n', stdout); 280} 281 282void clearerr(FILE* stream) 283{ 284 assert(stream); 285 stream->eof = false; 286 stream->error = 0; 287} 288 289int ferror(FILE* stream) 290{ 291 return stream->error; 292} 293 294size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) 295{ 296 assert(stream); 297 if (!size) 298 return 0; 299 300 ssize_t nread = 0; 301 302 if (stream->have_ungotten) { 303 // FIXME: Support ungotten character even if size != 1. 304 ASSERT(size == 1); 305 ((char*)ptr)[0] = stream->ungotten; 306 stream->have_ungotten = false; 307 --nmemb; 308 if (!nmemb) 309 return 1; 310 ptr = &((char*)ptr)[1]; 311 ++nread; 312 } 313 314 ssize_t rc = read(stream->fd, ptr, nmemb * size); 315 if (rc < 0) { 316 stream->error = errno; 317 return 0; 318 } 319 if (rc == 0) 320 stream->eof = true; 321 nread += rc; 322 return nread / size; 323} 324 325size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) 326{ 327 assert(stream); 328 auto* bytes = (const u8*)ptr; 329 ssize_t nwritten = 0; 330 for (size_t i = 0; i < (size * nmemb); ++i) { 331 int rc = fputc(bytes[i], stream); 332 if (rc == EOF) 333 break; 334 ++nwritten; 335 } 336 return nwritten / size; 337} 338 339int fseek(FILE* stream, long offset, int whence) 340{ 341 assert(stream); 342 fflush(stream); 343 off_t off = lseek(stream->fd, offset, whence); 344 if (off < 0) 345 return off; 346 stream->eof = false; 347 stream->error = 0; 348 stream->have_ungotten = false; 349 stream->ungotten = 0; 350 return 0; 351} 352 353long ftell(FILE* stream) 354{ 355 assert(stream); 356 fflush(stream); 357 return lseek(stream->fd, 0, SEEK_CUR); 358} 359 360int fgetpos(FILE* stream, fpos_t* pos) 361{ 362 assert(stream); 363 assert(pos); 364 365 long val = ftell(stream); 366 if (val == -1L) 367 return 1; 368 369 *pos = val; 370 return 0; 371} 372 373int fsetpos(FILE* stream, const fpos_t* pos) 374{ 375 assert(stream); 376 assert(pos); 377 return fseek(stream, (long) *pos, SEEK_SET); 378} 379 380void rewind(FILE* stream) 381{ 382 ASSERT(stream); 383 int rc = fseek(stream, 0, SEEK_SET); 384 ASSERT(rc == 0); 385} 386 387int dbgprintf(const char* fmt, ...) 388{ 389 va_list ap; 390 va_start(ap, fmt); 391 int ret = printf_internal([](char*&, char ch) { dbgputch(ch); }, nullptr, fmt, ap); 392 va_end(ap); 393 return ret; 394} 395 396[[gnu::always_inline]] inline void stdout_putch(char*&, char ch) 397{ 398 putchar(ch); 399} 400 401static FILE* __current_stream = nullptr; 402[[gnu::always_inline]] inline static void stream_putch(char*&, char ch) 403{ 404 fputc(ch, __current_stream); 405} 406 407int vfprintf(FILE* stream, const char* fmt, va_list ap) 408{ 409 __current_stream = stream; 410 return printf_internal(stream_putch, nullptr, fmt, ap); 411} 412 413int fprintf(FILE* stream, const char* fmt, ...) 414{ 415 va_list ap; 416 va_start(ap, fmt); 417 int ret = vfprintf(stream, fmt, ap); 418 va_end(ap); 419 return ret; 420} 421 422int vprintf(const char* fmt, va_list ap) 423{ 424 return printf_internal(stdout_putch, nullptr, fmt, ap); 425} 426 427int printf(const char* fmt, ...) 428{ 429 va_list ap; 430 va_start(ap, fmt); 431 int ret = vprintf(fmt, ap); 432 va_end(ap); 433 return ret; 434} 435 436static void buffer_putch(char*& bufptr, char ch) 437{ 438 *bufptr++ = ch; 439} 440 441int vsprintf(char* buffer, const char* fmt, va_list ap) 442{ 443 int ret = printf_internal(buffer_putch, buffer, fmt, ap); 444 buffer[ret] = '\0'; 445 return ret; 446} 447 448int sprintf(char* buffer, const char* fmt, ...) 449{ 450 va_list ap; 451 va_start(ap, fmt); 452 int ret = vsprintf(buffer, fmt, ap); 453 va_end(ap); 454 return ret; 455} 456 457static size_t __vsnprintf_space_remaining; 458[[gnu::always_inline]] inline void sized_buffer_putch(char*& bufptr, char ch) 459{ 460 if (__vsnprintf_space_remaining) { 461 *bufptr++ = ch; 462 --__vsnprintf_space_remaining; 463 } 464} 465 466int vsnprintf(char* buffer, size_t size, const char* fmt, va_list ap) 467{ 468 __vsnprintf_space_remaining = size; 469 int ret = printf_internal(sized_buffer_putch, buffer, fmt, ap); 470 if (__vsnprintf_space_remaining) { 471 buffer[ret] = '\0'; 472 } 473 return ret; 474} 475 476int snprintf(char* buffer, size_t size, const char* fmt, ...) 477{ 478 va_list ap; 479 va_start(ap, fmt); 480 int ret = vsnprintf(buffer, size, fmt, ap); 481 va_end(ap); 482 return ret; 483} 484 485void perror(const char* s) 486{ 487 int saved_errno = errno; 488 dbg() << "perror(): " << s << ": " << strerror(saved_errno); 489 fprintf(stderr, "%s: %s\n", s, strerror(saved_errno)); 490} 491 492FILE* fopen(const char* pathname, const char* mode) 493{ 494 int flags = 0; 495 // NOTE: rt is a non-standard mode which opens a file for read, explicitly 496 // specifying that it's a text file 497 if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt")) 498 flags = O_RDONLY; 499 else if (!strcmp(mode, "r+") || !strcmp(mode, "rb+")) 500 flags = O_RDWR; 501 else if (!strcmp(mode, "w") || !strcmp(mode, "wb")) 502 flags = O_WRONLY | O_CREAT | O_TRUNC; 503 else if (!strcmp(mode, "w+") || !strcmp(mode, "wb+")) 504 flags = O_RDWR | O_CREAT | O_TRUNC; 505 else if (!strcmp(mode, "a") || !strcmp(mode, "ab")) 506 flags = O_WRONLY | O_APPEND | O_CREAT; 507 else if (!strcmp(mode, "a+") || !strcmp(mode, "ab+")) 508 flags = O_RDWR | O_APPEND | O_CREAT; 509 else { 510 fprintf(stderr, "FIXME(LibC): fopen('%s', '%s')\n", pathname, mode); 511 ASSERT_NOT_REACHED(); 512 } 513 int fd = open(pathname, flags, 0666); 514 if (fd < 0) 515 return nullptr; 516 return make_FILE(fd); 517} 518 519FILE* freopen(const char* pathname, const char* mode, FILE* stream) 520{ 521 (void)pathname; 522 (void)mode; 523 (void)stream; 524 ASSERT_NOT_REACHED(); 525} 526 527FILE* fdopen(int fd, const char* mode) 528{ 529 UNUSED_PARAM(mode); 530 // FIXME: Verify that the mode matches how fd is already open. 531 if (fd < 0) 532 return nullptr; 533 return make_FILE(fd); 534} 535 536int fclose(FILE* stream) 537{ 538 fflush(stream); 539 int rc = close(stream->fd); 540 if (stream != &__default_streams[0] && stream != &__default_streams[1] && stream != &__default_streams[2]) 541 free(stream); 542 return rc; 543} 544 545int rename(const char* oldpath, const char* newpath) 546{ 547 if (!oldpath || !newpath) { 548 errno = EFAULT; 549 return -1; 550 } 551 Syscall::SC_rename_params params { { oldpath, strlen(oldpath) }, { newpath, strlen(newpath) } }; 552 int rc = syscall(SC_rename, &params); 553 __RETURN_WITH_ERRNO(rc, rc, -1); 554} 555 556void dbgputch(char ch) 557{ 558 syscall(SC_dbgputch, ch); 559} 560 561int dbgputstr(const char* characters, int length) 562{ 563 int rc = syscall(SC_dbgputstr, characters, length); 564 __RETURN_WITH_ERRNO(rc, rc, -1); 565} 566 567char* tmpnam(char*) 568{ 569 ASSERT_NOT_REACHED(); 570} 571 572FILE* popen(const char* command, const char* type) 573{ 574 if (!type || (*type != 'r' && *type != 'w')) { 575 errno = EINVAL; 576 return nullptr; 577 } 578 579 int pipe_fds[2]; 580 581 int rc = pipe(pipe_fds); 582 if (rc < 0) { 583 ScopedValueRollback rollback(errno); 584 perror("pipe"); 585 return nullptr; 586 } 587 588 pid_t child_pid = fork(); 589 if (!child_pid) { 590 if (*type == 'r') { 591 int rc = dup2(pipe_fds[1], STDOUT_FILENO); 592 if (rc < 0) { 593 perror("dup2"); 594 exit(1); 595 } 596 close(pipe_fds[0]); 597 close(pipe_fds[1]); 598 } else if (*type == 'w') { 599 int rc = dup2(pipe_fds[0], STDIN_FILENO); 600 if (rc < 0) { 601 perror("dup2"); 602 exit(1); 603 } 604 close(pipe_fds[0]); 605 close(pipe_fds[1]); 606 } 607 608 int rc = execl("/bin/sh", "sh", "-c", command, nullptr); 609 if (rc < 0) 610 perror("execl"); 611 exit(1); 612 } 613 614 FILE* fp = nullptr; 615 if (*type == 'r') { 616 fp = make_FILE(pipe_fds[0]); 617 close(pipe_fds[1]); 618 } else if (*type == 'w') { 619 fp = make_FILE(pipe_fds[1]); 620 close(pipe_fds[0]); 621 } 622 623 fp->popen_child = child_pid; 624 return fp; 625} 626 627int pclose(FILE* fp) 628{ 629 ASSERT(fp); 630 ASSERT(fp->popen_child != 0); 631 632 int wstatus = 0; 633 int rc = waitpid(fp->popen_child, &wstatus, 0); 634 if (rc < 0) 635 return rc; 636 637 return wstatus; 638} 639 640int remove(const char* pathname) 641{ 642 int rc = unlink(pathname); 643 if (rc < 0 && errno != EISDIR) 644 return -1; 645 return rmdir(pathname); 646} 647 648int scanf(const char* fmt, ...) 649{ 650 va_list ap; 651 va_start(ap, fmt); 652 int count = vfscanf(stdin, fmt, ap); 653 va_end(ap); 654 return count; 655} 656 657int fscanf(FILE* stream, const char* fmt, ...) 658{ 659 va_list ap; 660 va_start(ap, fmt); 661 int count = vfscanf(stream, fmt, ap); 662 va_end(ap); 663 return count; 664} 665 666int sscanf(const char* buffer, const char* fmt, ...) 667{ 668 va_list ap; 669 va_start(ap, fmt); 670 int count = vsscanf(buffer, fmt, ap); 671 va_end(ap); 672 return count; 673} 674 675int vfscanf(FILE* stream, const char* fmt, va_list ap) 676{ 677 char buffer[BUFSIZ]; 678 if (!fgets(buffer, sizeof(buffer) - 1, stream)) 679 return -1; 680 return vsscanf(buffer, fmt, ap); 681} 682 683void flockfile(FILE* filehandle) 684{ 685 (void)filehandle; 686 dbgprintf("FIXME: Implement flockfile()\n"); 687} 688 689void funlockfile(FILE* filehandle) 690{ 691 (void)filehandle; 692 dbgprintf("FIXME: Implement funlockfile()\n"); 693} 694 695FILE* tmpfile() 696{ 697 char tmp_path[] = "/tmp/XXXXXX"; 698 if (__generate_unique_filename(tmp_path) < 0) 699 return nullptr; 700 701 int fd = open(tmp_path, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); 702 if (fd < 0) 703 return nullptr; 704 705 // FIXME: instead of using this hack, implement with O_TMPFILE or similar 706 unlink(tmp_path); 707 708 return make_FILE(fd); 709} 710}