diff --git a/utils/.gitignore b/utils/.gitignore index 3004a910..3eb275b8 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -21,6 +21,7 @@ expr false find getaddrinfo +getty halt head help diff --git a/utils/Makefile b/utils/Makefile index 912f45f1..3af9a334 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -93,6 +93,7 @@ uname.1 \ SBINS=\ chroot \ +getty \ unmount \ MANPAGES8=\ diff --git a/utils/getty.c b/utils/getty.c new file mode 100644 index 00000000..31424d94 --- /dev/null +++ b/utils/getty.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and 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 OF7 + * 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. + * + * getty.c + * Initialize a terminal session. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + bool logo = true; + struct winsize ws; + ws.ws_row = 24; + ws.ws_col = 80; + int opt; + while ( (opt = getopt(argc, argv, "h:lw:")) != -1 ) + { + switch ( opt ) + { + case 'l': logo = true; break; + case 'h': ws.ws_row = atoi(optarg); break; + case 'w': ws.ws_col = atoi(optarg); break; + default: return 1; + } + } + if ( argc - optind < 1 ) + errx(1, "Expected terminal path"); + if ( argc - optind < 2 ) + errx(1, "Expected program"); + const char* path = argv[optind + 0]; + int tty = open(path, O_RDWR); + if ( tty < 0 ) + err(1, "%s", path); + if ( !isatty(tty) ) + err(1, "%s", path); + struct termios tio; + if ( tcgetattr(tty, &tio) < 0 ) + err(1, "tcgetattr: %s", path); + pid_t child_pid = fork(); + if ( child_pid < 0 ) + err(1, "fork"); + if ( child_pid ) + { + int status; + waitpid(child_pid, &status, 0); + return WEXITSTATUS(status); + } + if ( setsid() < 0 ) + err(1, "setsid"); + if ( ioctl(tty, TIOCSCTTY) < 0 ) + err(1, "ioctl: TIOCSCTTY"); + if ( close(0) < 0 || close(1) < 0 || close(2) < 0 ) + err(1, "close"); + if ( dup2(tty, 0) != 0 || + dup2(tty, 1) != 1 || + dup2(tty, 2) != 2 ) + err(1, "dup"); + if ( closefrom(3) < 0 ) + err(1, "closefrom"); + tty = 0; + if ( ioctl(tty, TIOCSWINSZ, &ws) < 0 ) + err(1, "TIOCSWINSZ"); + tio.c_cflag |= CREAD; + if ( tcsetattr(tty, TCSANOW, &tio) < 0 ) + err(1, "tcsetattr: %s", path); + if ( logo ) + { + printf("\e[37;41m\e[J"); + const char* string = BRAND_LOGO; + while ( *string ) + { + size_t string_width = strcspn(string, "\n"); + size_t leading = string_width <= ws.ws_col ? + (ws.ws_col - string_width) / 2 : 0; + for ( size_t i = 0; i < leading; i++ ) + putchar(' '); + fwrite(string, string_width, 1, stdout); + string += string_width; + if ( *string == '\n' ) + { + string++; + if ( !*string ) + printf("\e[m"); + putchar('\n'); + } + } + } + execvp(argv[optind + 1], argv + optind + 1); + err(1, "%s", argv[optind + 1]); +}