/* * getty.c - main code * Part of getty(8) for MiNT by Dave Gymer * Copyright (C) 1991 D P Gymer. All rights reserved. * $Id: getty.c,v 0.1 1992/01/08 15:58:56 dpg Exp dpg $ * $Log: getty.c,v $ * Revision 0.1 1992/01/08 15:58:56 dpg * Added select(2) call (at least temporarily) so that lines on which this * call works will not tie up CPU time. * Added support for environmental changes. Still need to add the `ev' * capability. * * Revision 0.0 1991/11/24 19:43:44 dpg * First experimental version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define GETTYTAB "/etc/gettytab" #ifndef LOGIN #define LOGIN "/usr/bin/login" #endif extern int putenv(char *); extern char **environ; #ifdef atarist int __default_mode__ = 1; /* All binary streams, please. */ #endif struct gettytab { struct tchars gt_tchars; struct ltchars gt_ltchars; struct sgttyb gt_sgttyb; char prompt[32]; char banner[80]; char hostname[32]; char login[FILENAME_MAX]; char next[32]; }; /* In case we get signalled, eg. alarm. */ static struct sgttyb save_sgttyb; /* * This isn't terribly efficient, but then it doesn't have to be, since * getty is only here until someone logs on. */ static int my_putenv(char *string) { if (!(string = strdup(string))) perror("getty: malloc"); else putenv(string); } /* * Take the current hostname and the hostname editing string and * produce a new hostname. */ static void hostname_edit(char *hostname, const char *edit) { char oldname[32], *name; strcpy((name = oldname), hostname); do { switch (*edit) { case '@': /* Add a character. */ if (*name) *hostname++ = *name++; break; case '#': /* Skip a character. */ if (*name) ++name; break; default: /* Includes the null terminator. */ *hostname++ = *edit; } } while (*edit++); } /* * Deal with the entry, setting the table entries as required. */ static void deal_with_entry(char *entbuf, struct gettytab *buf) { char sbuf[128], termvar[64], *s = sbuf; int n; if (tgetstr("nx", &s)) strcpy(buf -> next, (s = sbuf)); else *buf -> next = '\0'; if (tgetstr("hn", &s)) strcpy(buf -> hostname, (s = sbuf)); if (tgetstr("he", &s)) hostname_edit(buf -> hostname, (s = sbuf)); if (tgetstr("lm", &s)) strcpy(buf -> prompt, (s = sbuf)); if (tgetstr("lo", &s)) strcpy(buf -> login, (s = sbuf)); if (tgetstr("im", &s)) strcpy(buf -> banner, (s = sbuf)); if ((n = tgetnum("to")) != -1) alarm(n); if (tgetstr("tt", &s)) { sprintf(termvar, "TERM=%s", (s = sbuf)); my_putenv(termvar); } } /* * Get a termcap entry from a named file. */ static int tgetent_other(char *entbuf, char *type, const char *file) { extern char **environ; char **oldenv = environ, envbuf[128], *tmpenv[] = {envbuf, 0}, retval; sprintf(envbuf, "TERMCAP=%s", file); environ = tmpenv; retval = tgetent(entbuf, type); environ = oldenv; return retval; } /* * Find the entry for a particular type and store it in buf. * If no entry is found, use some defaults. */ static void get_getttytab(char *type, struct gettytab *buf) { char entbuf[2048]; switch (tgetent_other(entbuf, type, GETTYTAB)) { case -1: case 0: sprintf(entbuf, "getty: non-existant type `%s'\r\n", type); write(0, entbuf, strlen(entbuf)); break; default: deal_with_entry(entbuf, buf); } } /* * Format a string. * This consists mainly of substituting for %h and the like. * BUGS * - doesn't check for the end of the string buffer */ static char * fmtstring(const char *s, const struct gettytab *tab) { static char buf[128]; char *bp = buf; while (*s) if (*s == '%') { switch (*++s) { case '\0': goto out; case 'h': strcpy(bp, tab -> hostname); while (*bp) ++bp; break; case 't': strcpy(bp, ttyname(0)); while (*bp) ++bp; break; default: *bp = *s; } ++s; } else *bp++ = *s++; out: *bp = '\0'; return buf; } /* * Read the login name. * If break is pressed (or what we think is probably break), we try to * go to the next entry, by returning zero. */ static int read_name(char *name, struct gettytab *buf, int canselect) { char *s, c; long fds = 1; struct sgttyb tmpsg = buf -> gt_sgttyb; extern long select(int, long *, long *, long *, struct timeval *); tmpsg.sg_flags |= RAW; tmpsg.sg_flags &= ~(ECHO | CRMOD); stty(0, &tmpsg); s = fmtstring(buf -> prompt, buf); write(0, s, strlen(s)); s = name; if (canselect) select(1, &fds, 0, 0, 0); while (read(0, &c, 1) == 1 && c != '\n' && c != '\r') { if (!c || c & 128 || c == (buf -> gt_tchars).t_brkc) return 0; else if (c == (buf -> gt_sgttyb).sg_erase) { if (s > name) { --s; write(0, &(buf -> gt_sgttyb).sg_erase, 1); write(0, " ", 1); write(0, &(buf -> gt_sgttyb).sg_erase, 1); } } else { write(0, &c, 1); *s++ = c; } } /* Did we end with \n or \r? If the latter, we want EOL mapping. */ if (c == '\n') (buf -> gt_sgttyb).sg_flags &= ~CRMOD; else (buf -> gt_sgttyb).sg_flags |= CRMOD; *s = '\0'; write(0, "\r\n", 2); /* Did the name contain any lowercase letters? Assume not. */ s = name; (buf -> gt_sgttyb).sg_flags &= ~LCASE; do if (islower(*s)) { (buf -> gt_sgttyb).sg_flags |= LCASE; break; } while (*s++); return 1; } static void do_alarm(long sig) { const char *s = "Connection timed out.\r\n"; ioctl(0, TIOCSETP, &save_sgttyb); write(0, s, strlen(s)); exit(1); } int main(int argc, char **argv) { struct gettytab tab; char *type = 0, *tty, buf[256]; int fd, canselect, banner_flag = 1; struct stat info; extern int gethostname(char *, int); extern int execle(const char *, const char *,...); extern char *ttyname(int); ioctl(0, TIOCGETP, &save_sgttyb); signal(SIGALRM, do_alarm); /* Check for arguments. We ignore all but the last two. */ if (argc > 2) { /* a tty was specified */ close(0); fd = open((tty = argv[--argc]), O_RDWR); if (fd) { dup2(fd, 0); close(fd); fd = 0; } } else tty = ttyname(fd = 0); if (argc > 1) /* a type was specified */ type = argv[--argc]; if (fd < -1 || fd > 2) close(fd); #ifdef atarist dup2(fd, -1); /* Controlling terminal. */ #endif dup2(fd, 1); dup2(fd, 2); /* Set up some defaults for the terminal type. */ ioctl(0, TIOCGETP, &tab.gt_sgttyb); ioctl(0, TIOCGETC, &tab.gt_tchars); ioctl(0, TIOCGLTC, &tab.gt_ltchars); strcpy(tab.prompt, "login: "); strcpy(tab.banner, "%h running MiNT: line %t"); strcpy(tab.login, LOGIN); *tab.next = '\0'; gethostname(tab.hostname, 32); /* Get any default entries in the table. */ get_getttytab("default", &tab); /* Fselect(2) does not yet work on BIOS devices except the console. */ /* Probably when this works right, we won't need select anyway!!! */ if (!strcmp("/dev/console", tty) || !fstat(0, &info) && info.st_mode & S_IFMT != S_IFCHR) canselect = 1; else canselect = 0; /* Set up terminal type and read name until happy. */ for (;;) { if (type && *type) { /* Type to look up? */ get_getttytab(type, &tab); ioctl(0, TIOCSETP, &tab.gt_sgttyb); ioctl(0, TIOCSETC, &tab.gt_tchars); ioctl(0, TIOCSLTC, &tab.gt_ltchars); } if (banner_flag) { /* Print the banner? */ char *s; s = fmtstring(tab.banner, &tab); write(0, s, strlen(s)); banner_flag = 0; } if (!read_name(buf, &tab, canselect)) { /* Try next entry. */ if (tab.next) type = tab.next; else type = 0; banner_flag = 1; } else if (*buf)/* Got a name. */ break; } ioctl(0, TIOCSETP, &tab.gt_sgttyb); /* May have changed some. */ /* Call login. If LOGIN is in use, preserve $TERM. */ if (strcmp(LOGIN, tab.login)) execle(tab.login, tab.login, buf, (char *) 0, environ); else execle(tab.login, tab.login, "-p", buf, (char *) 0, environ); sprintf(buf, "getty: can't exec (%s) - %s\r\n", tab.login, errno > 0 && errno <= sys_nerr ? strerror(errno) : "???"); write(0, buf, strlen(buf)); return 1; }