diff --git a/examples/script.c b/examples/script.c new file mode 100644 index 000000000..e738ffdee --- /dev/null +++ b/examples/script.c @@ -0,0 +1,522 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright (c) 2010, 2012 David E. O'Brien │ +│ Copyright (c) 1980, 1992, 1993 │ +│ The Regents of the University of California. All rights reserved. │ +│ │ +│ Redistribution and use in source and binary forms, with or without │ +│ modification, are permitted provided that the following conditions │ +│ are met: │ +│ 1. Redistributions of source code must retain the above copyright │ +│ notice, this list of conditions and the following disclaimer. │ +│ 2. Redistributions in binary form must reproduce the above copyright │ +│ notice, this list of conditions and the following disclaimer in the │ +│ documentation and/or other materials provided with the distribution. │ +│ 3. Neither the name of the University nor the names of its contributors │ +│ may be used to endorse or promote products derived from this software │ +│ without specific prior written permission. │ +│ │ +│ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND │ +│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │ +│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE │ +│ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE │ +│ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL │ +│ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS │ +│ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) │ +│ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT │ +│ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY │ +│ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF │ +│ SUCH DAMAGE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/iovec.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/termios.h" +#include "libc/calls/struct/timeval.h" +#include "libc/calls/struct/winsize.h" +#include "libc/calls/termios.h" +#include "libc/calls/weirdtypes.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/intrin/bswap.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/bsd.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/paths.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/select.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/s.h" +#include "libc/sysv/consts/termios.h" +#include "libc/time/time.h" +#include "third_party/getopt/getopt.h" +// clang-format off + +/** + * @fileoverview Terminal Screencast Recorder / Player, e.g. + * + * make o//examples/script.com + * o//examples/script.com -r + * # type stuff.. + * # CTRL-D + * o//examples/script.com -p typescript + * + * @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS + * @see https://asciinema.org/ + */ + +asm(".ident\t\"\\n\\n\ +FreeBSD Script (BSD-3 License)\\n\ +Copyright (c) 2010, 2012 David E. O'Brien\\n\ +Copyright (c) 1980, 1992, 1993\\n\ +\tThe Regents of the University of California.\\n\ +\tAll rights reserved.\""); +asm(".include \"libc/disclaimer.inc\""); + +#define DEF_BUF 65536 + +struct stamp { + uint64_t scr_len; /* amount of data */ + uint64_t scr_sec; /* time it arrived in seconds... */ + uint32_t scr_usec; /* ...and microseconds */ + uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */ +}; + +static FILE *fscript; +static int master, slave; +static int child; +static const char *fname; +static char *fmfname; +static int qflg, ttyflg; +static int usesleep, rawout, showexit; + +static struct termios tt; + +static void done(int) wontreturn; +static void doshell(char **); +static void finish(void); +static void record(FILE *, char *, size_t, int); +static void consume(FILE *, off_t, char *, int); +static void playback(FILE *) wontreturn; +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int cc; + struct termios rtt, stt; + struct winsize win; + struct timeval tv, *tvp; + time_t tvec, start; + static char obuf[BUFSIZ]; + static char ibuf[BUFSIZ]; + fd_set rfd; + int aflg, Fflg, kflg, pflg, ch, k, n; + int flushtime, readstdin; + int fm_fd, fm_log; + + aflg = Fflg = kflg = pflg = 0; + usesleep = 1; + rawout = 0; + flushtime = 30; + fm_fd = -1; /* Shut up stupid "may be used uninitialized" GCC + warning. (not needed w/clang) */ + showexit = 0; + + while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1) + switch(ch) { + case 'a': + aflg = 1; + break; + case 'd': + usesleep = 0; + break; + case 'e': /* Default behavior, accepted for linux compat */ + break; + case 'F': + Fflg = 1; + break; + case 'k': + kflg = 1; + break; + case 'p': + pflg = 1; + break; + case 'q': + qflg = 1; + break; + case 'r': + rawout = 1; + break; + case 't': + flushtime = atoi(optarg); + if (flushtime < 0) + err(1, "invalid flush time %d", flushtime); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 0) { + fname = argv[0]; + argv++; + argc--; + } else + fname = "typescript"; + + if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL) + err(1, "%s", fname); + + if (pflg) + playback(fscript); + + if (tcgetattr(STDIN_FILENO, &tt) == -1 || + ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1) { + if (errno != ENOTTY) /* For debugger. */ + err(1, "tcgetattr/ioctl"); + if (openpty(&master, &slave, NULL, NULL, NULL) == -1) + err(1, "openpty"); + } else { + if (openpty(&master, &slave, NULL, &tt, &win) == -1) + err(1, "openpty"); + ttyflg = 1; + } + + if (rawout) + record(fscript, NULL, 0, 's'); + + if (!qflg) { + tvec = time(NULL); + printf("Script started, output file is %s\n", fname); + if (!rawout) { + fprintf(fscript, "Script started on %s", + ctime(&tvec)); + if (argv[0]) { + showexit = 1; + fprintf(fscript, "Command: "); + for (k = 0 ; argv[k] ; ++k) + fprintf(fscript, "%s%s", k ? " " : "", + argv[k]); + fprintf(fscript, "\n"); + } + } + fflush(fscript); + } + if (ttyflg) { + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); + } + + child = fork(); + if (child < 0) { + warn("fork"); + done(1); + } + if (child == 0) { + doshell(argv); + } + close(slave); + + start = tvec = time(0); + readstdin = 1; + for (;;) { + FD_ZERO(&rfd); + FD_SET(master, &rfd); + if (readstdin) + FD_SET(STDIN_FILENO, &rfd); + if (!readstdin && ttyflg) { + tv.tv_sec = 1; + tv.tv_usec = 0; + tvp = &tv; + readstdin = 1; + } else if (flushtime > 0) { + tv.tv_sec = flushtime - (tvec - start); + tv.tv_usec = 0; + tvp = &tv; + } else { + tvp = NULL; + } + n = select(master + 1, &rfd, 0, 0, tvp); + if (n < 0 && errno != EINTR) + break; + if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) { + cc = read(STDIN_FILENO, ibuf, BUFSIZ); + if (cc < 0) + break; + if (cc == 0) { + if (tcgetattr(master, &stt) == 0 && + (stt.c_lflag & ICANON) != 0) { + write(master, &stt.c_cc[VEOF], 1); + } + readstdin = 0; + } + if (cc > 0) { + if (rawout) + record(fscript, ibuf, cc, 'i'); + write(master, ibuf, cc); + if (kflg && tcgetattr(master, &stt) >= 0 && + ((stt.c_lflag & ECHO) == 0)) { + fwrite(ibuf, 1, cc, fscript); + } + } + } + if (n > 0 && FD_ISSET(master, &rfd)) { + cc = read(master, obuf, sizeof (obuf)); + if (cc <= 0) + break; + write(STDOUT_FILENO, obuf, cc); + if (rawout) + record(fscript, obuf, cc, 'o'); + else + fwrite(obuf, 1, cc, fscript); + } + tvec = time(0); + if (tvec - start >= flushtime) { + fflush(fscript); + start = tvec; + } + if (Fflg) + fflush(fscript); + } + finish(); + done(0); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: script [-adfkpqr] [-t time] [file [command ...]]\n"); + exit(1); +} + +static void +finish(void) +{ + int e, status; + + if (waitpid(child, &status, 0) == child) { + if (WIFEXITED(status)) + e = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + e = WTERMSIG(status); + else /* can't happen */ + e = 1; + done(e); + } +} + +static void +doshell(char **av) +{ + const char *shell; + + shell = getenv("SHELL"); + if (shell == NULL) + shell = _PATH_BSHELL; + + close(master); + fclose(fscript); + free(fmfname); + login_tty(slave); + setenv("SCRIPT", fname, 1); + if (av[0]) { + execvp(av[0], av); + warn("%s", av[0]); + } else { + execl(shell, shell, "-i", 0); + warn("%s", shell); + } + exit(1); +} + +static void +done(int eno) +{ + time_t tvec; + + if (ttyflg) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt); + tvec = time(NULL); + if (rawout) + record(fscript, NULL, 0, 'e'); + if (!qflg) { + if (!rawout) { + if (showexit) + fprintf(fscript, "\nCommand exit status:" + " %d", eno); + fprintf(fscript,"\nScript done on %s", + ctime(&tvec)); + } + printf("\nScript done, output file is %s\n", fname); + } + fclose(fscript); + close(master); + exit(eno); +} + +static void +record(FILE *fp, char *buf, size_t cc, int direction) +{ + struct iovec iov[2]; + struct stamp stamp; + struct timeval tv; + + gettimeofday(&tv, NULL); + stamp.scr_len = cc; + stamp.scr_sec = tv.tv_sec; + stamp.scr_usec = tv.tv_usec; + stamp.scr_direction = direction; + iov[0].iov_len = sizeof(stamp); + iov[0].iov_base = &stamp; + iov[1].iov_len = cc; + iov[1].iov_base = buf; + if (writev(fileno(fp), &iov[0], 2) == -1) + err(1, "writev"); +} + +static void +consume(FILE *fp, off_t len, char *buf, int reg) +{ + size_t l; + + if (reg) { + if (fseeko(fp, len, SEEK_CUR) == -1) + err(1, NULL); + } + else { + while (len > 0) { + l = MIN(DEF_BUF, len); + if (fread(buf, sizeof(char), l, fp) != l) + err(1, "cannot read buffer"); + len -= l; + } + } +} + +#define swapstamp(stamp) do { \ + if (stamp.scr_direction > 0xff) { \ + stamp.scr_len = bswap_64(stamp.scr_len); \ + stamp.scr_sec = bswap_64(stamp.scr_sec); \ + stamp.scr_usec = bswap_32(stamp.scr_usec); \ + stamp.scr_direction = bswap_32(stamp.scr_direction); \ + } \ +} while (0/*CONSTCOND*/) + +static void +termset(void) +{ + struct termios traw; + + if (tcgetattr(STDOUT_FILENO, &tt) == -1) { + if (errno != ENOTTY) /* For debugger. */ + err(1, "tcgetattr"); + return; + } + ttyflg = 1; + traw = tt; + cfmakeraw(&traw); + traw.c_lflag |= ISIG; + tcsetattr(STDOUT_FILENO, TCSANOW, &traw); +} + +static void +termreset(void) +{ + if (ttyflg) { + tcsetattr(STDOUT_FILENO, TCSADRAIN, &tt); + ttyflg = 0; + } +} + +static void +playback(FILE *fp) +{ + struct timespec tsi, tso; + struct stamp stamp; + struct stat pst; + static char buf[DEF_BUF]; + off_t nread, save_len; + size_t l; + time_t tclock; + int reg; + + if (fstat(fileno(fp), &pst) == -1) + err(1, "fstat failed"); + + reg = S_ISREG(pst.st_mode); + + for (nread = 0; !reg || nread < pst.st_size; nread += save_len) { + if (fread(&stamp, sizeof(stamp), 1, fp) != 1) { + if (reg) + err(1, "reading playback header"); + else + break; + } + swapstamp(stamp); + save_len = sizeof(stamp); + + if (reg && stamp.scr_len > + (uint64_t)(pst.st_size - save_len) - nread) + errx(1, "invalid stamp"); + + save_len += stamp.scr_len; + tclock = stamp.scr_sec; + tso.tv_sec = stamp.scr_sec; + tso.tv_nsec = stamp.scr_usec * 1000; + + switch (stamp.scr_direction) { + case 's': + if (!qflg) + printf("Script started on %s", + ctime(&tclock)); + tsi = tso; + consume(fp, stamp.scr_len, buf, reg); + termset(); + atexit(termreset); + break; + case 'e': + termreset(); + if (!qflg) + printf("\nScript done on %s", + ctime(&tclock)); + consume(fp, stamp.scr_len, buf, reg); + break; + case 'i': + /* throw input away */ + consume(fp, stamp.scr_len, buf, reg); + break; + case 'o': + tsi.tv_sec = tso.tv_sec - tsi.tv_sec; + tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec; + if (tsi.tv_nsec < 0) { + tsi.tv_sec -= 1; + tsi.tv_nsec += 1000000000; + } + if (usesleep) + nanosleep(&tsi, NULL); + tsi = tso; + while (stamp.scr_len > 0) { + l = MIN(DEF_BUF, stamp.scr_len); + if (fread(buf, sizeof(char), l, fp) != l) + err(1, "cannot read buffer"); + + write(STDOUT_FILENO, buf, l); + stamp.scr_len -= l; + } + break; + default: + errx(1, "invalid direction"); + } + } + fclose(fp); + exit(0); +} diff --git a/examples/script.txt b/examples/script.txt new file mode 100644 index 000000000..9164af5a1 --- /dev/null +++ b/examples/script.txt @@ -0,0 +1,100 @@ +SCRIPT(1) Cosmopolitan General Commands Manual SCRIPT(1) + +𝐍𝐀𝐌𝐄 + 𝘀𝗰𝗿𝗶𝗽𝘁 — make typescript of terminal session + +𝐒𝐘𝐍𝐎𝐏𝐒𝐈𝐒 + 𝘀𝗰𝗿𝗶𝗽𝘁 [-𝗮𝗱𝗲𝗳𝗸𝗽𝗾𝗿] [-𝐅 p̲i̲p̲e̲] [-𝘁 t̲i̲m̲e̲] [f̲i̲l̲e̲ [c̲o̲m̲m̲a̲n̲d̲ .̲.̲.̲]] + +𝐃𝐄𝐒𝐂𝐑𝐈𝐏𝐓𝐈𝐎𝐍 + The 𝘀𝗰𝗿𝗶𝗽𝘁 utility makes a typescript of everything printed on your termi‐ + nal. It is useful for students who need a hardcopy record of an interac‐ + tive session as proof of an assignment, as the typescript file can be + printed out later with lpr(1). + + If the argument f̲i̲l̲e̲ is given, 𝘀𝗰𝗿𝗶𝗽𝘁 saves all dialogue in f̲i̲l̲e̲. If no + file name is given, the typescript is saved in the file t̲y̲p̲e̲s̲c̲r̲i̲p̲t̲. + + If the argument c̲o̲m̲m̲a̲n̲d̲ is given, 𝘀𝗰𝗿𝗶𝗽𝘁 will run the specified command + with an optional argument vector instead of an interactive shell. + + The following options are available: + + -𝗮 Append the output to f̲i̲l̲e̲ or t̲y̲p̲e̲s̲c̲r̲i̲p̲t̲, retaining the prior con‐ + tents. + + -𝗱 When playing back a session with the -𝗽 flag, do not sleep between + records when playing back a timestamped session. + + -𝗲 Accepted for compatibility with u̲t̲i̲l̲-̲l̲i̲n̲u̲x̲ 𝘀𝗰𝗿𝗶𝗽𝘁. The child com‐ + mand exit status is always the exit status of 𝘀𝗰𝗿𝗶𝗽𝘁. + + -𝐅 p̲i̲p̲e̲ + Immediately flush output after each write. This will allow a user + to create a named pipe using mkfifo(1) and another user may watch + the live session using a utility like cat(1). + + -𝗸 Log keys sent to the program as well as output. + + -𝗽 Play back a session recorded with the -𝗿 flag in real time. + + -𝗾 Run in quiet mode, omit the start, stop and command status mes‐ + sages. + + -𝗿 Record a session with input, output, and timestamping. + + -𝘁 t̲i̲m̲e̲ + Specify the interval at which the script output file will be + flushed to disk, in seconds. A value of 0 causes 𝘀𝗰𝗿𝗶𝗽𝘁 to flush + after every character I/O event. The default interval is 30 sec‐ + onds. + + The script ends when the forked shell (or command) exits (a c̲o̲n̲t̲r̲o̲l̲-̲D̲ to + exit the Bourne shell (sh(1)), and e̲x̲i̲t̲, l̲o̲g̲o̲u̲t̲ or c̲o̲n̲t̲r̲o̲l̲-̲D̲ (if i̲g̲n̲o̲r̲e̲e̲o̲f̲ + is not set) for the C-shell, csh(1)). + + Certain interactive commands, such as vi(1), create garbage in the type‐ + script file. The 𝘀𝗰𝗿𝗶𝗽𝘁 utility works best with commands that do not ma‐ + nipulate the screen. The results are meant to emulate a hardcopy terminal, + not an addressable one. + +𝐄𝐍𝐕𝐈𝐑𝐎𝐍𝐌𝐄𝐍𝐓 + The following environment variables are utilized by 𝘀𝗰𝗿𝗶𝗽𝘁: + + SCRIPT + The SCRIPT environment variable is added to the sub-shell. If + SCRIPT already existed in the users environment, its value is over‐ + written within the sub-shell. The value of SCRIPT is the name of + the t̲y̲p̲e̲s̲c̲r̲i̲p̲t̲ file. + + SHELL If the variable SHELL exists, the shell forked by 𝘀𝗰𝗿𝗶𝗽𝘁 will be + that shell. If SHELL is not set, the Bourne shell is assumed. + (Most shells set this variable automatically). + +𝐒𝐄𝐄 𝐀𝐋𝐒𝐎 + csh(1) (for the h̲i̲s̲t̲o̲r̲y̲ mechanism) + +𝐇𝐈𝐒𝐓𝐎𝐑𝐘 + The 𝘀𝗰𝗿𝗶𝗽𝘁 command appeared in 3.0BSD. + + The -𝗱, -𝗽 and -𝗿 options first appeared in NetBSD 2.0 and were ported to + FreeBSD 9.2. + +𝐁𝐔𝐆𝐒 + The 𝘀𝗰𝗿𝗶𝗽𝘁 utility places 𝗲𝘃𝗲𝗿𝘆𝘁𝗵𝗶𝗻𝗴 in the log file, including linefeeds + and backspaces. This is not what the naive user expects. + + It is not possible to specify a command without also naming the script file + because of argument parsing compatibility issues. + + When running in -𝗸 mode, echo cancelling is far from ideal. The slave ter‐ + minal mode is checked for ECHO mode to check when to avoid manual echo log‐ + ging. This does not work when the terminal is in a raw mode where the pro‐ + gram being run is doing manual echo. + + If 𝘀𝗰𝗿𝗶𝗽𝘁 reads zero bytes from the terminal, it switches to a mode when it + only attempts to read once a second until there is data to read. This pre‐ + vents 𝘀𝗰𝗿𝗶𝗽𝘁 from spinning on zero-byte reads, but might cause a 1-second + delay in processing of user input. + +BSD September 1, 2020 BSD diff --git a/examples/whois.c b/examples/whois.c new file mode 100644 index 000000000..fbf960131 --- /dev/null +++ b/examples/whois.c @@ -0,0 +1,617 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright (c) 1980, 1993 │ +│ The Regents of the University of California. All rights reserved. │ +│ │ +│ Redistribution and use in source and binary forms, with or without │ +│ modification, are permitted provided that the following conditions │ +│ are met: │ +│ 1. Redistributions of source code must retain the above copyright │ +│ notice, this list of conditions and the following disclaimer. │ +│ 2. Redistributions in binary form must reproduce the above copyright │ +│ notice, this list of conditions and the following disclaimer in the │ +│ documentation and/or other materials provided with the distribution. │ +│ 3. Neither the name of the University nor the names of its contributors │ +│ may be used to endorse or promote products derived from this software │ +│ without specific prior written permission. │ +│ │ +│ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND │ +│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │ +│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE │ +│ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE │ +│ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL │ +│ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS │ +│ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) │ +│ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT │ +│ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY │ +│ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF │ +│ SUCH DAMAGE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dns/dns.h" +#include "libc/errno.h" +#include "libc/log/bsd.h" +#include "libc/mem/fmt.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/struct/pollfd.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/poll.h" +#include "libc/sysv/consts/sock.h" +#include "third_party/getopt/getopt.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +FreeBSD Whois (BSD-3 License)\\n\ +Copyright (c) 1980, 1993\\n\ +\tThe Regents of the University of California.\\n\ +\tAll rights reserved.\""); +asm(".include \"libc/disclaimer.inc\""); + +#define ABUSEHOST "whois.abuse.net" +#define ANICHOST "whois.arin.net" +#define DENICHOST "whois.denic.de" +#define DKNICHOST "whois.dk-hostmaster.dk" +#define FNICHOST "whois.afrinic.net" +#define GNICHOST "whois.nic.gov" +#define IANAHOST "whois.iana.org" +#define INICHOST "whois.internic.net" +#define KNICHOST "whois.krnic.net" +#define LNICHOST "whois.lacnic.net" +#define MNICHOST "whois.ra.net" +#define PDBHOST "whois.peeringdb.com" +#define PNICHOST "whois.apnic.net" +#define QNICHOST_TAIL ".whois-servers.net" +#define RNICHOST "whois.ripe.net" +#define VNICHOST "whois.verisign-grs.com" + +#define DEFAULT_PORT "whois" + +#define WHOIS_RECURSE 0x01 +#define WHOIS_QUICK 0x02 +#define WHOIS_SPAM_ME 0x04 + +#define CHOPSPAM ">>> Last update of WHOIS database:" + +#define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') + +#define SCAN(p, end, check) \ + while ((p) < (end)) \ + if (check) ++(p); \ + else break + +static struct { + const char *suffix, *server; +} whoiswhere[] = { + /* Various handles */ + { "-ARIN", ANICHOST }, + { "-NICAT", "at" QNICHOST_TAIL }, + { "-NORID", "no" QNICHOST_TAIL }, + { "-RIPE", RNICHOST }, + /* Nominet's whois server doesn't return referrals to JANET */ + { ".ac.uk", "ac.uk" QNICHOST_TAIL }, + { ".gov.uk", "ac.uk" QNICHOST_TAIL }, + { "", IANAHOST }, /* default */ + { NULL, NULL } /* safety belt */ +}; + +#define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 } +static struct { + const char *prefix; + size_t len; +} whois_referral[] = { + WHOIS_REFERRAL("whois:"), /* IANA */ + WHOIS_REFERRAL("Whois Server:"), + WHOIS_REFERRAL("Registrar WHOIS Server:"), /* corporatedomains.com */ + WHOIS_REFERRAL("ReferralServer: whois://"), /* ARIN */ + WHOIS_REFERRAL("ReferralServer: rwhois://"), /* ARIN */ + WHOIS_REFERRAL("descr: region. Please query"), /* AfriNIC */ + { NULL, 0 } +}; + +/* + * We have a list of patterns for RIRs that assert ignorance rather than + * providing referrals. If that happens, we guess that ARIN will be more + * helpful. But, before following a referral to an RIR, we check if we have + * asked that RIR already, and if so we make another guess. + */ +static const char *actually_arin[] = { + "netname: ERX-NETBLOCK\n", /* APNIC */ + "netname: NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK\n", + NULL +}; + +static struct { + int loop; + const char *host; +} try_rir[] = { + { 0, ANICHOST }, + { 0, RNICHOST }, + { 0, PNICHOST }, + { 0, FNICHOST }, + { 0, LNICHOST }, + { 0, NULL } +}; + +static void +reset_rir(void) { + int i; + + for (i = 0; try_rir[i].host != NULL; i++) + try_rir[i].loop = 0; +} + +static const char *port = DEFAULT_PORT; + +static const char *choose_server(char *); +static struct addrinfo *gethostinfo(const char *, const char *, int); +static void s_asprintf(char **ret, const char *format, ...); +static void usage(void); +static void whois(const char *, const char *, const char *, int); + +int +main(int argc, char *argv[]) +{ + const char *country, *host; + int ch, flags; + +#ifdef SOCKS + SOCKSinit(argv[0]); +#endif + + country = host = NULL; + flags = 0; + while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) { + switch (ch) { + case 'a': + host = ANICHOST; + break; + case 'A': + host = PNICHOST; + break; + case 'b': + host = ABUSEHOST; + break; + case 'c': + country = optarg; + break; + case 'f': + host = FNICHOST; + break; + case 'g': + host = GNICHOST; + break; + case 'h': + host = optarg; + break; + case 'i': + host = INICHOST; + break; + case 'I': + host = IANAHOST; + break; + case 'k': + host = KNICHOST; + break; + case 'l': + host = LNICHOST; + break; + case 'm': + host = MNICHOST; + break; + case 'p': + port = optarg; + break; + case 'P': + host = PDBHOST; + break; + case 'Q': + flags |= WHOIS_QUICK; + break; + case 'r': + host = RNICHOST; + break; + case 'R': + flags |= WHOIS_RECURSE; + break; + case 'S': + flags |= WHOIS_SPAM_ME; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (!argc || (country != NULL && host != NULL)) + usage(); + + /* + * If no host or country is specified, rely on referrals from IANA. + */ + if (host == NULL && country == NULL) { + if ((host = getenv("WHOIS_SERVER")) == NULL && + (host = getenv("RA_SERVER")) == NULL) { + if (!(flags & WHOIS_QUICK)) + flags |= WHOIS_RECURSE; + } + } + while (argc-- > 0) { + if (country != NULL) { + char *qnichost; + s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); + whois(*argv, qnichost, port, flags); + free(qnichost); + } else + whois(*argv, host != NULL ? host : + choose_server(*argv), port, flags); + reset_rir(); + argv++; + } + exit(0); +} + +static const char * +choose_server(char *domain) +{ + size_t len = strlen(domain); + int i; + + for (i = 0; whoiswhere[i].suffix != NULL; i++) { + size_t suffix_len = strlen(whoiswhere[i].suffix); + if (len > suffix_len && + strcasecmp(domain + len - suffix_len, + whoiswhere[i].suffix) == 0) + return (whoiswhere[i].server); + } + errx(EX_SOFTWARE, "no default whois server"); +} + +static struct addrinfo * +gethostinfo(const char *host, const char *hport, int exit_on_noname) +{ + struct addrinfo hints, *res; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + res = NULL; + error = getaddrinfo(host, hport, &hints, &res); + if (error && (exit_on_noname || error != EAI_NONAME)) + err(EX_NOHOST, "%s: %s", host, gai_strerror(error)); + return (res); +} + +/* + * Wrapper for asprintf(3) that exits on error. + */ +static void +s_asprintf(char **ret, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if (vasprintf(ret, format, ap) == -1) { + va_end(ap); + err(EX_OSERR, "vasprintf()"); + } + va_end(ap); +} + +static int +connect_to_any_host(struct addrinfo *hostres) +{ + struct addrinfo *res; + nfds_t i, j; + size_t count; + struct pollfd *fds; + int timeout = 180, s = -1; + + for (res = hostres, count = 0; res; res = res->ai_next) + count++; + fds = calloc(count, sizeof(*fds)); + if (fds == NULL) + err(EX_OSERR, "calloc()"); + + /* + * Traverse the result list elements and make non-block + * connection attempts. + */ + count = i = 0; + for (res = hostres; res != NULL; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, + res->ai_protocol); + if (s < 0) + continue; + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (errno == EINPROGRESS) { + /* Add the socket to poll list */ + fds[i].fd = s; + fds[i].events = POLLERR | POLLHUP | + POLLIN | POLLOUT; + /* + * From here until a socket connects, the + * socket fd is owned by the fds[] poll array. + */ + s = -1; + count++; + i++; + } else { + close(s); + s = -1; + + /* + * Poll only if we have something to poll, + * otherwise just go ahead and try next + * address + */ + if (count == 0) + continue; + } + } else + goto done; + + /* + * If we are at the last address, poll until a connection is + * established or we failed all connection attempts. + */ + if (res->ai_next == NULL) + timeout = INFTIM; + + /* + * Poll the watched descriptors for successful connections: + * if we still have more untried resolved addresses, poll only + * once; otherwise, poll until all descriptors have errors, + * which will be considered as ETIMEDOUT later. + */ + do { + int n; + + n = poll(fds, i, timeout); + if (n == 0) { + /* + * No event reported in time. Try with a + * smaller timeout (but cap at 2-3ms) + * after a new host have been added. + */ + if (timeout >= 3) + timeout >>= 1; + + break; + } else if (n < 0) { + /* + * errno here can only be EINTR which we would + * want to clean up and bail out. + */ + s = -1; + goto done; + } + + /* + * Check for the event(s) we have seen. + */ + for (j = 0; j < i; j++) { + if (fds[j].fd == -1 || fds[j].events == 0 || + fds[j].revents == 0) + continue; + if (fds[j].revents & ~(POLLIN | POLLOUT)) { + close(fds[j].fd); + fds[j].fd = -1; + fds[j].events = 0; + count--; + continue; + } else if (fds[j].revents & (POLLIN | POLLOUT)) { + /* Connect succeeded. */ + s = fds[j].fd; + fds[j].fd = -1; + + goto done; + } + + } + } while (timeout == INFTIM && count != 0); + } + + /* All attempts were failed */ + s = -1; + if (count == 0) + errno = ETIMEDOUT; + +done: + /* Close all watched fds except the succeeded one */ + for (j = 0; j < i; j++) + if (fds[j].fd != -1) + close(fds[j].fd); + free(fds); + return (s); +} + +static void +whois(const char *query, const char *hostname, const char *hostport, int flags) +{ + FILE *fp; + struct addrinfo *hostres; + char *buf, *host, *nhost, *nport, *p; + int comment, s, f; + size_t len, i; + + hostres = gethostinfo(hostname, hostport, 1); + s = connect_to_any_host(hostres); + if (s == -1) + err(EX_OSERR, "connect()"); + + /* Restore default blocking behavior. */ + if ((f = fcntl(s, F_GETFL)) == -1) + err(EX_OSERR, "fcntl()"); + f &= ~O_NONBLOCK; + if (fcntl(s, F_SETFL, f) == -1) + err(EX_OSERR, "fcntl()"); + + fp = fdopen(s, "r+"); + if (fp == NULL) + err(EX_OSERR, "fdopen()"); + + if (!(flags & WHOIS_SPAM_ME) && + (strcasecmp(hostname, DENICHOST) == 0 || + strcasecmp(hostname, "de" QNICHOST_TAIL) == 0)) { + const char *q; + int idn = 0; + for (q = query; *q != '\0'; q++) + if (!isascii(*q)) + idn = 1; + fprintf(fp, "-T dn%s %s\r\n", idn ? "" : ",ace", query); + } else if (!(flags & WHOIS_SPAM_ME) && + (strcasecmp(hostname, DKNICHOST) == 0 || + strcasecmp(hostname, "dk" QNICHOST_TAIL) == 0)) + fprintf(fp, "--show-handles %s\r\n", query); + else if ((flags & WHOIS_SPAM_ME) || + strchr(query, ' ') != NULL) + fprintf(fp, "%s\r\n", query); + else if (strcasecmp(hostname, ANICHOST) == 0) { + if (strncasecmp(query, "AS", 2) == 0 && + strspn(query+2, "0123456789") == strlen(query+2)) + fprintf(fp, "+ a %s\r\n", query+2); + else + fprintf(fp, "+ %s\r\n", query); + } else if (strcasecmp(hostres->ai_canonname, VNICHOST) == 0) + fprintf(fp, "domain %s\r\n", query); + else + fprintf(fp, "%s\r\n", query); + fflush(fp); + + comment = 0; + if (!(flags & WHOIS_SPAM_ME) && + (strcasecmp(hostname, ANICHOST) == 0 || + strcasecmp(hostname, RNICHOST) == 0)) { + comment = 2; + } + + nhost = NULL; + while ((buf = fgetln(fp, &len)) != NULL) { + /* Nominet */ + if (!(flags & WHOIS_SPAM_ME) && + len == 5 && strncmp(buf, "-- \r\n", 5) == 0) + break; + /* RIRs */ + if (comment == 1 && buf[0] == '#') + break; + else if (comment == 2) { + if (strchr("#%\r\n", buf[0]) != NULL) + continue; + else + comment = 1; + } + + printf("%.*s", (int)len, buf); + + if ((flags & WHOIS_RECURSE) && nhost == NULL) { + for (i = 0; whois_referral[i].prefix != NULL; i++) { + p = buf; + SCAN(p, buf+len, *p == ' '); + if (strncasecmp(p, whois_referral[i].prefix, + whois_referral[i].len) != 0) + continue; + p += whois_referral[i].len; + SCAN(p, buf+len, *p == ' '); + host = p; + SCAN(p, buf+len, ishost(*p)); + if (p > host) { + char *pstr; + + s_asprintf(&nhost, "%.*s", + (int)(p - host), host); + + if (*p != ':') { + s_asprintf(&nport, "%s", port); + break; + } + + pstr = ++p; + SCAN(p, buf+len, isdigit(*p)); + if (p > pstr && (p - pstr) < 6) { + s_asprintf(&nport, "%.*s", + (int)(p - pstr), pstr); + break; + } + + /* Invalid port; don't recurse */ + free(nhost); + nhost = NULL; + } + break; + } + for (i = 0; actually_arin[i] != NULL; i++) { + if (strncmp(buf, actually_arin[i], len) == 0) { + s_asprintf(&nhost, "%s", ANICHOST); + s_asprintf(&nport, "%s", port); + break; + } + } + } + /* Verisign etc. */ + if (!(flags & WHOIS_SPAM_ME) && + len >= sizeof(CHOPSPAM)-1 && + (strncasecmp(buf, CHOPSPAM, sizeof(CHOPSPAM)-1) == 0 || + strncasecmp(buf, CHOPSPAM+4, sizeof(CHOPSPAM)-5) == 0)) { + printf("\n"); + break; + } + } + fclose(fp); + freeaddrinfo(hostres); + + f = 0; + for (i = 0; try_rir[i].host != NULL; i++) { + /* Remember visits to RIRs */ + if (try_rir[i].loop == 0 && + strcasecmp(try_rir[i].host, hostname) == 0) + try_rir[i].loop = 1; + /* Do we need to find an alternative RIR? */ + if (try_rir[i].loop != 0 && nhost != NULL && + strcasecmp(try_rir[i].host, nhost) == 0) { + free(nhost); + nhost = NULL; + free(nport); + nport = NULL; + f = 1; + } + } + if (f) { + /* Find a replacement RIR */ + for (i = 0; try_rir[i].host != NULL; i++) { + if (try_rir[i].loop == 0) { + s_asprintf(&nhost, "%s", try_rir[i].host); + s_asprintf(&nport, "%s", port); + break; + } + } + } + if (nhost != NULL) { + /* Ignore self-referrals */ + if (strcasecmp(hostname, nhost) != 0) { + printf("# %s\n\n", nhost); + whois(query, nhost, nport, flags); + } + free(nhost); + free(nport); + } +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] " + "[-p port] name ...\n"); + exit(EX_USAGE); +} diff --git a/libc/calls/grantpt.c b/libc/calls/grantpt.c index 2b0e6226d..697d22886 100644 --- a/libc/calls/grantpt.c +++ b/libc/calls/grantpt.c @@ -16,6 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" +#include "libc/sysv/consts/termios.h" -int grantpt(int fd) { return 0; } +/** + * Grants access to subordinate pseudoteletypewriter. + * + * @return 0 on success, or -1 w/ errno + * @raise EBADF if fd isn't open + * @raise EINVAL if fd is valid but not associated with pty + * @raise EACCES if pseudoterminal couldn't be accessed + */ +int grantpt(int fd) { + int rc; + if (IsXnu()) { + rc = sys_ioctl(fd, TIOCPTYGRANT); + } else { + rc = _isptmaster(fd); + } + STRACE("grantpt(%d) → %d% m", fd, rc); + return rc; +} diff --git a/libc/calls/internal.h b/libc/calls/internal.h index df73d7599..498b94e5d 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -12,13 +12,6 @@ COSMOPOLITAN_C_START_ #define kIoMotion ((const int8_t[3]){1, 0, 0}) -struct IoctlPtmGet { - int theduxfd; - int workerfd; - char theduxname[16]; - char workername[16]; -}; - hidden extern struct Fds g_fds; hidden extern const struct Fd kEmptyFd; diff --git a/libc/log/errx.c b/libc/calls/isptmaster.c similarity index 79% rename from libc/log/errx.c rename to libc/calls/isptmaster.c index 9b13c9658..0b4c2eb58 100644 --- a/libc/log/errx.c +++ b/libc/calls/isptmaster.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,11 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" +#include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/consts/termios.h" -wontreturn void(errx)(int eval, const char *fmt, ...) { - va_list va; - va_start(va, fmt); - (verrx)(eval, fmt, va); - va_end(va); +int _isptmaster(int fd) { + if (IsFreebsd()) { + if (!sys_ioctl(fd, TIOCPTMASTER)) { + return 0; + } else { + if (errno != EBADF) { + errno = EINVAL; + } + return -1; + } + } else { + return 0; + } } diff --git a/libc/calls/openpty.c b/libc/calls/openpty.c index 49ae1c638..e48548b82 100644 --- a/libc/calls/openpty.c +++ b/libc/calls/openpty.c @@ -18,12 +18,23 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" +#include "libc/calls/struct/termios.h" +#include "libc/calls/struct/winsize.h" #include "libc/calls/termios.h" -#include "libc/fmt/itoa.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/rop.h" +#include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pty.h" #include "libc/sysv/consts/termios.h" -#include "libc/sysv/errfuns.h" + +struct IoctlPtmGet { + int m; + int s; + char mname[16]; + char sname[16]; +}; /** * Opens new pseudo teletypewriter. @@ -37,24 +48,29 @@ */ int openpty(int *mfd, int *sfd, char *name, const struct termios *tio, const struct winsize *wsz) { - int m, s, n; - char buf[20]; - if ((m = open("/dev/ptmx", O_RDWR | O_NOCTTY)) != -1) { - n = 0; - if (!ioctl(m, TIOCSPTLCK, &n) && !ioctl(m, TIOCGPTN, &n)) { - if (!name) name = buf; - name[0] = '/', name[1] = 'd', name[2] = 'e', name[3] = 'v'; - name[4] = '/', name[5] = 'p', name[6] = 't', name[7] = 's'; - name[8] = '/', FormatInt32(name + 9, n); - if ((s = open(name, O_RDWR | O_NOCTTY)) != -1) { - if (tio) ioctl(s, TCSETS, tio); - if (wsz) ioctl(s, TIOCSWINSZ, wsz); - *mfd = m; - *sfd = s; - return 0; - } - } + int m, s, p; + const char *t; + struct IoctlPtmGet ptm; + RETURN_ON_ERROR((m = posix_openpt(O_RDWR | O_NOCTTY))); + if (!IsOpenbsd()) { + RETURN_ON_ERROR(grantpt(m)); + RETURN_ON_ERROR(unlockpt(m)); + if (!(t = ptsname(m))) goto OnError; + RETURN_ON_ERROR((s = open(t, O_RDWR))); + } else { + RETURN_ON_ERROR(ioctl(m, PTMGET, &ptm)); close(m); + m = ptm.m; + s = ptm.s; + t = ptm.sname; } + *mfd = m; + *sfd = s; + if (name) strcpy(name, t); + if (tio) ioctl(s, TCSETSF, tio); + if (wsz) ioctl(s, TIOCSWINSZ, wsz); + return 0; +OnError: + if (m != -1) close(m); return -1; } diff --git a/libc/calls/posix_openpt.c b/libc/calls/posix_openpt.c index 09047ed45..1759287bd 100644 --- a/libc/calls/posix_openpt.c +++ b/libc/calls/posix_openpt.c @@ -16,43 +16,35 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/pty.h" -#include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" /** * Opens new pseudo teletypewriter. * + * @return fd of master pty, or -1 w/ errno * @params flags is usually O_RDWR|O_NOCTTY * @return file descriptor, or -1 w/ errno */ int posix_openpt(int flags) { - int fd, ilduce; - struct IoctlPtmGet ptm; - if ((flags & O_ACCMODE) != O_RDWR) return einval(); - if (SupportsFreebsd() && - ((fd = sys_posix_openpt(flags)) != -1 || errno != ENOSYS)) { - return fd; - } else if ((fd = open("/dev/ptmx", flags)) != -1 || errno != ENOENT) { - return fd; - } else if (SupportsOpenbsd() && - ((fd = open("/dev/ptm", flags)) != -1 || errno != ENOENT)) { - if (ioctl(fd, PTMGET, &ptm) != -1) { - close(ptm.workerfd); - ilduce = ptm.theduxfd; - } else { - ilduce = -1; - } - close(fd); - return ilduce; + int rc; + if ((flags & O_ACCMODE) != O_RDWR) { + rc = einval(); + } else if (IsLinux() || IsXnu()) { + rc = sys_open("/dev/ptmx", flags, 0); + } else if (IsOpenbsd()) { + rc = sys_open("/dev/ptm", flags, 0); + } else if (IsFreebsd()) { + rc = sys_posix_openpt(flags); + if (rc == -1 && errno == ENOSPC) errno = EAGAIN; } else { - return enosys(); + rc = enosys(); } + STRACE("posix_openpt(%#o) → %d% m", flags, rc); + return rc; } diff --git a/libc/calls/ptsname.c b/libc/calls/ptsname.c index 28eed523c..7dfccca41 100644 --- a/libc/calls/ptsname.c +++ b/libc/calls/ptsname.c @@ -17,9 +17,25 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/termios.h" +#include "libc/errno.h" +#include "libc/intrin/strace.internal.h" -static char g_ptsname[32]; +static char g_ptsname[256]; +/** + * Gets name subordinate pseudoteletypewriter. + * + * @return static string path on success, or NULL w/ errno + */ char *ptsname(int fd) { - return ptsname_r(fd, g_ptsname, sizeof(g_ptsname)) ? g_ptsname : NULL; + char *res; + errno_t e; + if (!(e = ptsname_r(fd, g_ptsname, sizeof(g_ptsname)))) { + res = g_ptsname; + } else { + errno = e; + res = 0; + } + STRACE("ptsname(%d) → %s% m", fd, res); + return res; } diff --git a/libc/calls/ptsname_r.c b/libc/calls/ptsname_r.c index baa0ea8e7..be373d9d6 100644 --- a/libc/calls/ptsname_r.c +++ b/libc/calls/ptsname_r.c @@ -17,23 +17,69 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" +#include "libc/paths.h" #include "libc/str/str.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" -errno_t ptsname_r(int fd, char *buf, size_t size) { - int pty; - char tb[32]; - if (size) { - if (!buf) return einval(); - if (ioctl(fd, TIOCGPTN, &pty) == -1) return errno; - FormatInt32(stpcpy(tb, "/dev/pts/"), pty); - if (strlen(tb) + 1 >= size) return (errno = ERANGE); - stpcpy(buf, tb); - /* TODO(jart): OpenBSD OMG */ +struct fiodgname_arg { + int len; + void *buf; +}; + +static int PtsName(int fd, char *buf, size_t size) { + if (size < 9 + 12) return erange(); + if (_isptmaster(fd)) return -1; + + if (IsLinux()) { + int pty; + if (sys_ioctl(fd, TIOCGPTN, &pty)) return -1; + buf[0] = '/', buf[1] = 'd', buf[2] = 'e', buf[3] = 'v'; + buf[4] = '/', buf[5] = 'p', buf[6] = 't', buf[7] = 's'; + buf[8] = '/', FormatInt32(buf + 9, pty); + return 0; } - return 0; + + if (IsFreebsd()) { + struct fiodgname_arg fgn = {size - 5, buf + 5}; + buf[0] = '/', buf[1] = 'd'; + buf[2] = 'e', buf[3] = 'v'; + buf[4] = '/', buf[5] = 0; + if (sys_ioctl(fd, FIODGNAME, &fgn) == -1) { + if (errno == EINVAL) errno = ERANGE; + return -1; + } + return 0; + } + + if (IsXnu()) { + char b2[128]; + if (sys_ioctl(fd, TIOCPTYGNAME, b2)) return -1; + if (strlen(b2) + 1 > size) return erange(); + strcpy(buf, b2); + return 0; + } + + return enosys(); +} + +/** + * Gets name subordinate pseudoteletypewriter. + * + * @return 0 on success, or errno on error + */ +errno_t ptsname_r(int fd, char *buf, size_t size) { + int rc, e = errno; + if (!PtsName(fd, buf, size)) { + rc = 0; + } else { + rc = errno; + errno = e; + } + return rc; } diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h index 3a400d7ed..11f0bbdc8 100644 --- a/libc/calls/syscall-sysv.internal.h +++ b/libc/calls/syscall-sysv.internal.h @@ -116,6 +116,7 @@ i64 sys_readlinkat(int, const char *, char *, u64) hidden; i64 sys_sendfile(i32, i32, i64 *, u64) hidden; i64 sys_splice(i32, i64 *, i32, i64 *, u64, u32) hidden; i64 sys_write(i32, const void *, u64) hidden; +int _isptmaster(int); u32 sys_getegid(void) hidden; u32 sys_geteuid(void) hidden; u32 sys_getgid(void) hidden; diff --git a/libc/calls/unlockpt.c b/libc/calls/unlockpt.c index 84f980632..59ba673a2 100644 --- a/libc/calls/unlockpt.c +++ b/libc/calls/unlockpt.c @@ -17,11 +17,32 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/consts/pty.h" +#include "libc/sysv/errfuns.h" +/** + * Unlocks pseudoteletypewriter pair. + * + * @return 0 on success, or -1 w/ errno + * @raise EBADF if fd isn't open + * @raise EINVAL if fd is valid but not associated with pty + */ int unlockpt(int fd) { - int unlock = 0; - /* TODO(jart) */ - return ioctl(fd, TIOCSPTLCK, &unlock); + int rc; + if (IsFreebsd() || IsOpenbsd()) { + rc = _isptmaster(fd); + } else if (IsXnu()) { + rc = sys_ioctl(fd, TIOCPTYUNLK); + } else if (IsLinux()) { + int unlock = 0; + rc = sys_ioctl(fd, TIOCSPTLCK, &unlock); + } else { + rc = enosys(); + } + STRACE("unlockpt(%d) → %d% m", fd, rc); + return rc; } diff --git a/libc/fmt/unbing.c b/libc/fmt/unbing.c index c2f6ed86c..0de72eaa2 100644 --- a/libc/fmt/unbing.c +++ b/libc/fmt/unbing.c @@ -16,9 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/alg.h" #include "libc/macros.internal.h" +#include "libc/mem/alg.h" #include "libc/nexgen32e/nexgen32e.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" static const int kCp437iMultimappings[] = { @@ -76,7 +77,7 @@ static textstartup void g_cp437i_init() { unsigned i; for (i = 0; i < 256; ++i) g_cp437i[i] = kCp437[i] << 8 | i; memcpy(g_cp437i + 256, kCp437iMultimappings, sizeof(kCp437iMultimappings)); - djbsort(g_cp437i, ARRAYLEN(g_cp437i)); + _intsort(g_cp437i, ARRAYLEN(g_cp437i)); } const void *const g_cp437i_ctor[] initarray = {g_cp437i_init}; diff --git a/libc/isystem/stdio.h b/libc/isystem/stdio.h index cff507487..a9a67e076 100644 --- a/libc/isystem/stdio.h +++ b/libc/isystem/stdio.h @@ -2,6 +2,7 @@ #define LIBC_ISYSTEM_STDIO_H_ #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" +#include "libc/mem/fmt.h" #include "libc/stdio/lock.internal.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" diff --git a/libc/isystem/sysexits.h b/libc/isystem/sysexits.h new file mode 100644 index 000000000..7e52e2c87 --- /dev/null +++ b/libc/isystem/sysexits.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SYSEXITS_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_SYSEXITS_H_ +#include "libc/sysv/consts/ex.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYSEXITS_H_ */ diff --git a/libc/log/bsd.h b/libc/log/bsd.h index 10cf20712..8f9842182 100644 --- a/libc/log/bsd.h +++ b/libc/log/bsd.h @@ -1,30 +1,21 @@ #ifndef COSMOPOLITAN_LIBC_LOG_BSD_H_ #define COSMOPOLITAN_LIBC_LOG_BSD_H_ -#include "libc/fmt/pflink.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § logging » berkeley logger ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -void err(int, const char *, ...) printfesque(2) wontreturn; -void errx(int, const char *, ...) printfesque(2) wontreturn; -void verr(int, const char *, va_list) paramsnonnull((3)) wontreturn; -void verrx(int, const char *, va_list) paramsnonnull((3)) wontreturn; -void vwarn(const char *, va_list) paramsnonnull((2)); -void vwarnx(const char *, va_list) paramsnonnull((2)); -void warn(const char *, ...) printfesque(1); -void warnx(const char *, ...) printfesque(1); - -#define err(EVAL, FMT, ...) (err)(EVAL, PFLINK(FMT), ##__VA_ARGS__) -#define errx(EVAL, FMT, ...) (errx)(EVAL, PFLINK(FMT), ##__VA_ARGS__) -#define verr(EVAL, FMT, VA) (verr)(EVAL, PFLINK(FMT), VA) -#define verrx(EVAL, FMT, VA) (verrx)(EVAL, PFLINK(FMT), VA) -#define vwarn(FMT, VA) (vwarn)(PFLINK(FMT), VA) -#define vwarnx(FMT, VA) (vwarnx)(PFLINK(FMT), VA) -#define warn(FMT, ...) (warn)(PFLINK(FMT), ##__VA_ARGS__) -#define warnx(FMT, ...) (warn)(PFLINK(FMT), ##__VA_ARGS__) +void err(int, const char *, ...) wontreturn; +void verr(int, const char *, va_list) wontreturn; +void errc(int, int, const char *, ...) wontreturn; +void verrc(int, int, const char *, va_list) wontreturn; +void errx(int, const char *, ...) wontreturn; +void verrx(int, const char *, va_list) wontreturn; +void warn(const char *, ...); +void vwarn(const char *, va_list); +void warnc(int, const char *, ...); +void vwarnc(int, const char *, va_list); +void warnx(const char *, ...); +void vwarnx(const char *, va_list); +void err_set_exit(void (*)(int)); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/err.c b/libc/log/err.c index 91eb020ec..6ef4ecf5b 100644 --- a/libc/log/err.c +++ b/libc/log/err.c @@ -1,26 +1,194 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright (c) 1993 │ +│ The Regents of the University of California. All rights reserved. │ │ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ +│ Redistribution and use in source and binary forms, with or without │ +│ modification, are permitted provided that the following conditions │ +│ are met: │ +│ 1. Redistributions of source code must retain the above copyright │ +│ notice, this list of conditions and the following disclaimer. │ +│ 2. Redistributions in binary form must reproduce the above copyright │ +│ notice, this list of conditions and the following disclaimer in the │ +│ documentation and/or other materials provided with the distribution. │ +│ 3. Neither the name of the University nor the names of its contributors │ +│ may be used to endorse or promote products derived from this software │ +│ without specific prior written permission. │ │ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ +│ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND │ +│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │ +│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE │ +│ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE │ +│ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL │ +│ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS │ +│ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) │ +│ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT │ +│ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY │ +│ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF │ +│ SUCH DAMAGE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/log/bsd.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +// clang-format off -wontreturn void(err)(int eval, const char *fmt, ...) { - va_list va; - va_start(va, fmt); - (verr)(eval, fmt, va); - va_end(va); +asm(".ident\t\"\\n\\n\ +FreeBSD Err (BSD-3 License)\\n\ +Copyright (c) 1993\\n\ +\tThe Regents of the University of California.\\n\ +\tAll rights reserved.\""); +asm(".include \"libc/disclaimer.inc\""); + +static FILE *err_file; /* file to use for error output */ +static void (*err_exit)(int); + +/* + * This is declared to take a `void *' so that the caller is not required + * to include first. However, it is really a `FILE *', and the + * manual page documents it as such. + */ +void +err_set_file(void *fp) +{ + if (fp) + err_file = fp; + else + err_file = stderr; +} + +void +err_set_exit(void (*ef)(int)) +{ + err_exit = ef; +} + +void +err(int eval, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrc(eval, errno, fmt, ap); + va_end(ap); +} + +void +verr(int eval, const char *fmt, va_list ap) +{ + verrc(eval, errno, fmt, ap); +} + +void +errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrc(eval, code, fmt, ap); + va_end(ap); +} + +void +verrc(int eval, int code, const char *fmt, va_list ap) +{ + if (err_file == NULL) + err_set_file(NULL); + fprintf(err_file, "%s: ", program_invocation_name); + if (fmt != NULL) { + vfprintf(err_file, fmt, ap); + fprintf(err_file, ": "); + } + fprintf(err_file, "%s\n", _strerdoc(code)); + if (err_exit) + err_exit(eval); + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrx(eval, fmt, ap); + va_end(ap); +} + +void +verrx(int eval, const char *fmt, va_list ap) +{ + if (err_file == NULL) + err_set_file(NULL); + fprintf(err_file, "%s: ", program_invocation_name); + if (fmt != NULL) + vfprintf(err_file, fmt, ap); + fprintf(err_file, "\n"); + if (err_exit) + err_exit(eval); + exit(eval); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnc(errno, fmt, ap); + va_end(ap); +} + +void +vwarn(const char *fmt, va_list ap) +{ + vwarnc(errno, fmt, ap); +} + +void +warnc(int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnc(code, fmt, ap); + va_end(ap); +} + +void +vwarnc(int code, const char *fmt, va_list ap) +{ + int saved_errno; + + saved_errno = errno; + if (err_file == NULL) + err_set_file(NULL); + fprintf(err_file, "%s: ", program_invocation_name); + if (fmt != NULL) { + vfprintf(err_file, fmt, ap); + fprintf(err_file, ": "); + } + fprintf(err_file, "%s\n", strerror(code)); + errno = saved_errno; +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarnx(const char *fmt, va_list ap) +{ + int saved_errno; + + saved_errno = errno; + if (err_file == NULL) + err_set_file(NULL); + fprintf(err_file, "%s: ", program_invocation_name); + if (fmt != NULL) + vfprintf(err_file, fmt, ap); + fprintf(err_file, "\n"); + errno = saved_errno; } diff --git a/libc/log/verrx.c b/libc/log/verrx.c deleted file mode 100644 index 0e8afb31f..000000000 --- a/libc/log/verrx.c +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" -#include "libc/log/color.internal.h" -#include "libc/log/internal.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" - -wontreturn void(verrx)(int eval, const char *fmt, va_list va) { - fprintf(stderr, "%s: %s%s%s: ", program_invocation_name, RED2, "ERROR", - RESET); - if (fmt) (vfprintf)(stderr, fmt, va); - fprintf(stderr, "\n"); - exit(eval); -} diff --git a/libc/log/vwarn.c b/libc/log/vwarn.c deleted file mode 100644 index c4529df8c..000000000 --- a/libc/log/vwarn.c +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" -#include "libc/log/color.internal.h" -#include "libc/log/internal.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" - -void(vwarn)(const char *fmt, va_list va) { - fprintf(stderr, "%s: %s%s%s[%m]: ", program_invocation_name, SUBTLE, - "WARNING", RESET); - if (fmt) (vfprintf)(stderr, fmt, va); - fprintf(stderr, "\n"); -} diff --git a/libc/log/warn.c b/libc/log/warn.c deleted file mode 100644 index 59a86510b..000000000 --- a/libc/log/warn.c +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" - -void(warn)(const char *fmt, ...) { - va_list va; - va_start(va, fmt); - (vwarn)(fmt, va); - va_end(va); -} diff --git a/libc/log/warnx.c b/libc/log/warnx.c deleted file mode 100644 index cc63f2598..000000000 --- a/libc/log/warnx.c +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" - -void(warnx)(const char *fmt, ...) { - va_list va; - va_start(va, fmt); - (vwarnx)(fmt, va); - va_end(va); -} diff --git a/libc/log/verr.c b/libc/runtime/login_tty.c similarity index 68% rename from libc/log/verr.c rename to libc/runtime/login_tty.c index b700b78a7..219790ca0 100644 --- a/libc/log/verr.c +++ b/libc/runtime/login_tty.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,18 +16,37 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/safemacros.internal.h" -#include "libc/log/bsd.h" -#include "libc/log/color.internal.h" -#include "libc/log/internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/ioctl.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" +#include "libc/runtime/utmp.h" +#include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" -wontreturn void(verr)(int eval, const char *fmt, va_list va) { - fprintf(stderr, - "%s: %s%s%s[%m]: ", firstnonnull(program_invocation_name, "unknown"), - RED2, "ERROR", RESET); - if (fmt) (vfprintf)(stderr, fmt, va); - fprintf(stderr, "\n"); - exit(eval); +/** + * Prepares terminal for login. + * + * @return 0 on success, or -1 w/ errno + * @raise ENOSYS on Windows and Metal + * @raise EPERM if terminal is already controlling another sid + */ +int login_tty(int fd) { + int rc; + if (IsLinux() || IsBsd()) { + setsid(); + if (!sys_ioctl(fd, TIOCSCTTY, 0)) { + for (int i = 0; i < 3; ++i) dup2(fd, i); + if (fd > 2) close(fd); + rc = 0; + } else { + rc = -1; + } + } else { + rc = enosys(); + } + STRACE("login_tty(%d) → %d% m", fd, rc); + return rc; } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 1c5108283..a39bfa922 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -88,12 +88,14 @@ long gethostid(void); int sethostid(long); char *getlogin(void); int getlogin_r(char *, size_t); +int login_tty(int); int getpagesize(void); int syncfs(int); int vhangup(void); int getdtablesize(void); int sethostname(const char *, size_t); int acct(const char *); +void _intsort(int *, size_t); void _longsort(long *, size_t); bool _isheap(void *); int NtGetVersion(void) pureconst; diff --git a/libc/log/vwarnx.c b/libc/str/intsort.c similarity index 74% rename from libc/log/vwarnx.c rename to libc/str/intsort.c index 10fc81c53..70b785ec7 100644 --- a/libc/log/vwarnx.c +++ b/libc/str/intsort.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,15 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/bsd.h" -#include "libc/log/color.internal.h" -#include "libc/log/internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -void(vwarnx)(const char *fmt, va_list va) { - fprintf(stderr, "%s: %s%s%s: ", program_invocation_name, SUBTLE, "WARNING", - RESET); - if (fmt) (vfprintf)(stderr, fmt, va); - fprintf(stderr, "\n"); +static void _intsorter(int *A, size_t n) { + int t, p; + size_t i, j; + if (n < 2) return; + for (p = A[n / 2], i = 0, j = n - 1;; i++, j--) { + while (A[i] < p) i++; + while (A[j] > p) j--; + if (i >= j) break; + t = A[i]; + A[i] = A[j]; + A[j] = t; + } + _intsorter(A, i); + _intsorter(A + i, n - i); +} + +/** + * Tiny and reasonably fast sorting for ints. + * @see djbsort + */ +void _intsort(int *A, size_t n) { + _intsorter(A, n); + if (n > 1000) { + STRACE("_intsort(%p, %'zu)", A, n); + } } diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 3df60aca6..f0fa3aa50 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1374,7 +1374,7 @@ syscon termios TIOCCONS 0x541d 0x80047462 0x80047462 0x80047462 0x800474 syscon termios TIOCGETD 0x5424 0x4004741a 0x4004741a 0x4004741a 0x4004741a 0 # boop syscon termios TIOCNOTTY 0x5422 0x20007471 0x20007471 0x20007471 0x20007471 0 # boop syscon termios TIOCNXCL 0x540d 0x2000740e 0x2000740e 0x2000740e 0x2000740e 0 # boop -syscon termios TIOCSCTTY 0x540e 0x20007461 0x20007461 0x20007461 0x20007461 0 # boop +syscon termios TIOCSCTTY 0x540e 0x20007461 0x20007461 0x20007461 0x20007461 0 # makes terminal controlling terminal of calling process (see login_tty) syscon termios TIOCSETD 0x5423 0x8004741b 0x8004741b 0x8004741b 0x8004741b 0 # boop syscon termios TIOCSIG 0x40045436 0x2000745f 0x2004745f 0x8004745f 0x8004745f 0 # boop syscon termios TIOCSTI 0x5412 0x80017472 0x80017472 0 0 0 # boop @@ -1409,6 +1409,10 @@ syscon termios TIOCSFLAGS 0 0 0 0x8004745c 0x8004745c 0 # boop syscon termios TIOCSTSTAMP 0 0 0 0x8008745a 0x8008745a 0 # boop syscon termios ENDRUNDISC 0 0 0 0x9 0x9 0 # boop syscon termios TIOCPTMASTER 0 0 0x2000741c 0 0 0 # boop +syscon termios TIOCPTYGRANT 0 0x20007454 0 0 0 0 # xnu grantpt() +syscon termios TIOCPTYUNLK 0 0x20007452 0 0 0 0 # xnu grantpt() +syscon termios TIOCPTYGNAME 0 0x40807453 0 0 0 0 # xnu grantpt() +syscon termios FIODGNAME 0 0 0x80106678 0 0 0 # freebsd ptsname_r() syscon termios NETGRAPHDISC 0 0 0x6 0 0 0 # boop syscon termios H4DISC 0 0 0x7 0 0 0 # boop diff --git a/libc/sysv/consts/FIODGNAME.s b/libc/sysv/consts/FIODGNAME.s new file mode 100644 index 000000000..0c0fb683d --- /dev/null +++ b/libc/sysv/consts/FIODGNAME.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/consts/syscon.internal.inc" +.syscon termios,FIODGNAME,0,0,0x80106678,0,0,0 diff --git a/libc/sysv/consts/TIOCPTYGNAME.s b/libc/sysv/consts/TIOCPTYGNAME.s new file mode 100644 index 000000000..cf2c3daf8 --- /dev/null +++ b/libc/sysv/consts/TIOCPTYGNAME.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/consts/syscon.internal.inc" +.syscon termios,TIOCPTYGNAME,0,0x40807453,0,0,0,0 diff --git a/libc/sysv/consts/TIOCPTYGRANT.s b/libc/sysv/consts/TIOCPTYGRANT.s new file mode 100644 index 000000000..6d2c03655 --- /dev/null +++ b/libc/sysv/consts/TIOCPTYGRANT.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/consts/syscon.internal.inc" +.syscon termios,TIOCPTYGRANT,0,0x20007454,0,0,0,0 diff --git a/libc/sysv/consts/TIOCPTYUNLK.s b/libc/sysv/consts/TIOCPTYUNLK.s new file mode 100644 index 000000000..6057d9277 --- /dev/null +++ b/libc/sysv/consts/TIOCPTYUNLK.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/consts/syscon.internal.inc" +.syscon termios,TIOCPTYUNLK,0,0x20007452,0,0,0,0 diff --git a/libc/sysv/consts/poll.h b/libc/sysv/consts/poll.h index 609e1602e..c25e4cb05 100644 --- a/libc/sysv/consts/poll.h +++ b/libc/sysv/consts/poll.h @@ -2,6 +2,7 @@ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_POLL_H_ #include "libc/runtime/symbolic.h" +#define INFTIM (-1) #define POLLERR SYMBOLIC(POLLERR) #define POLLHUP SYMBOLIC(POLLHUP) #define POLLIN SYMBOLIC(POLLIN) diff --git a/libc/sysv/consts/termios.h b/libc/sysv/consts/termios.h index 7d3fe9d5d..0059408cc 100644 --- a/libc/sysv/consts/termios.h +++ b/libc/sysv/consts/termios.h @@ -178,6 +178,10 @@ extern const uint8_t VTIME; extern const uint8_t VWERASE; extern const uint32_t XCASE; extern const uint32_t XTABS; +extern const uint32_t FIODGNAME; +extern const uint32_t TIOCPTYGRANT; +extern const uint32_t TIOCPTYUNLK; +extern const uint32_t TIOCPTYGNAME; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ @@ -250,6 +254,7 @@ COSMOPOLITAN_C_END_ #define EXTA SYMBOLIC(EXTA) #define EXTB SYMBOLIC(EXTB) #define EXTPROC SYMBOLIC(EXTPROC) +#define FIODGNAME SYMBOLIC(FIODGNAME) #define FLUSHO SYMBOLIC(FLUSHO) #define H4DISC SYMBOLIC(H4DISC) #define HUPCL SYMBOLIC(HUPCL) @@ -319,6 +324,9 @@ COSMOPOLITAN_C_END_ #define TIOCNXCL SYMBOLIC(TIOCNXCL) #define TIOCOUTQ SYMBOLIC(TIOCOUTQ) #define TIOCPTMASTER SYMBOLIC(TIOCPTMASTER) +#define TIOCPTYGNAME SYMBOLIC(TIOCPTYGNAME) +#define TIOCPTYGRANT SYMBOLIC(TIOCPTYGRANT) +#define TIOCPTYUNLK SYMBOLIC(TIOCPTYUNLK) #define TIOCREMOTE SYMBOLIC(TIOCREMOTE) #define TIOCSBRK SYMBOLIC(TIOCSBRK) #define TIOCSCTTY SYMBOLIC(TIOCSCTTY) diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 04d84ca6e..25bc94125 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -55,17 +55,16 @@ enum PosixThreadStatus { }; struct PosixThread { - void *(*start_routine)(void *); - void *arg; // start_routine's parameter - void *rc; // start_routine's return value - bool ownstack; - int tid; - int *ctid; - char *tls; - char *altstack; - struct CosmoTib *tib; _Atomic(enum PosixThreadStatus) status; - jmp_buf exiter; + void *(*start_routine)(void *); + void *arg; // start_routine's parameter + void *rc; // start_routine's return value + bool ownstack; // should we free it + int tid; // clone parent tid + char *altstack; // thread sigaltstack + char *tls; // bottom of tls allocation + struct CosmoTib *tib; // middle of tls allocation + jmp_buf exiter; // for pthread_exit pthread_attr_t attr; }; @@ -82,7 +81,7 @@ void _pthread_wait(struct PosixThread *) hidden; void _pthread_zombies_add(struct PosixThread *) hidden; void _pthread_zombies_decimate(void) hidden; void _pthread_zombies_harvest(void) hidden; -void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]); +void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/test/libc/sock/test.mk b/test/libc/sock/test.mk index dd62c814e..4d8f630c6 100644 --- a/test/libc/sock/test.mk +++ b/test/libc/sock/test.mk @@ -73,6 +73,9 @@ o/$(MODE)/test/libc/sock/socket_test.com.runs: .INTERNET = 1 # todo: ipv6 filte o/$(MODE)/test/libc/sock/socket_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc inet +o/$(MODE)/test/libc/sock/shutdown_test.com.runs: \ + private .PLEDGE = stdio rpath wpath cpath fattr proc inet + $(TEST_LIBC_SOCK_OBJS): test/libc/sock/test.mk .PHONY: o/$(MODE)/test/libc/sock