Hi, What a coincidence, even I was about to send getty implementation in this forum.
Attached is a little largeish version of getty. Supporting TERM settings, baud rate setting, issue file, updating utmp entries.... if this suits, can add to the tree. Looking forward to your inputs. regards, Ashwini On Thu, Dec 19, 2013 at 12:49 PM, <ibid...@gmail.com> wrote: > Here's a basic getty. > It just reopens the current tty in such a way as to allow blocking code > to work, then calls the login program of your choice (-l). > /etc/issue is ignored, so -i is a nop. > It does NOT modify the environment (including setting TERM), support > baud rate setting or autodetection, do anything with modems, prompt for > any user input, or open new ttys. > Supporting > getty ttyN $TERM > should be something like this... > char * prepend(char *arg, char *pfix) > { > if (!arg) return NULL; > int alen = strlen(arg), plen = strlen(pfix); > char * ret = xzalloc(alen + plen + 1); > strcpy(ret, pfix); > strcpy(ret+plen, arg); > return ret; > } > > > char *tty = prepend(toys.optargs[0], "/dev/") > if (!tty) tty = ttyname(0); > if (toys.optc > 1) { > char *termname = prepend(toys.optargs[1], "TERM="); > if (termname) putenv(termname); > } > > -- > With this, I can boot to a login prompt using only a shell and a toybox > binary (with several pending applets enabled, and a hacked version of > one of the many submissions for mount). > > I also have a small applet (resolve_modalias) that is mainly useful for > testing, which I've sent a couple times to illustrate issues; > maybe sometime I should see about putting the core code into a library > function so modprobe and modinfo can share it.... > > Thanks, > Isaac Dunham > > _______________________________________________ > Toybox mailing list > Toybox@lists.landley.net > http://lists.landley.net/listinfo.cgi/toybox-landley.net > >
/* getty.c - A getty program to get controlling terminal. * * Copyright 2012 Sandeep Sharma <sandeep.jack2...@gamil.com> * Copyright 2013 Kyungwan Han <asura...@gmail.com> * * No Standard. USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN)) config GETTY bool "getty" default n help Usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE] -h Enable hardware RTS/CTS flow control -L Set CLOCAL (ignore Carrier Detect state) -m Get baud rate from modem's CONNECT status message -n Don't prompt for login name -w Wait for CR or LF before sending /etc/issue -i Don't display /etc/issue -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue -l LOGIN Invoke LOGIN instead of /bin/login -t SEC Terminate after SEC if no login name is read -I INITSTR Send INITSTR before anything else -H HOST Log HOST into the utmp file as the hostname */ #define FOR_getty #include "toys.h" #include <utmp.h> GLOBALS( char *issue_str; char *login_str; char *init_str; char *host_str; long timeout; char *tty_name; int speeds[20]; int sc; struct termios termios; char buff[128]; ) #define CTL(x) ((x) ^ 0100) #define HOSTNAME_SIZE 32 typedef void (*sighandler_t)(int); struct speed_mapper { long speed; speed_t code; }; struct speed_mapper speedtab[] = { {50, B50}, {75, B75}, {110, B110}, {134, B134}, {150, B150}, {200, B200}, {300, B300}, {600, B600}, {1200, B1200}, {1800, B1800}, {2400, B2400}, {4800, B4800}, {9600, B9600}, #ifdef B19200 {19200, B19200}, #endif #ifdef B38400 {38400, B38400}, #endif #ifdef EXTA {19200, EXTA}, #endif #ifdef EXTB {38400, B38400}, #endif #ifdef B57600 {57600, B57600}, #endif #ifdef B115200 {115200, B115200}, #endif #ifdef B230400 {230400, B230400}, #endif {0, 0}, }; // Find speed from mapper array static speed_t encode(char *s) { struct speed_mapper *sp; long speed = atolx(s); if (!speed) return 0; for (sp = speedtab; sp->speed; sp++) if (sp->speed == speed) return sp->code; return (speed_t) -1; } static void get_speed(char *sp) { char *ptr; TT.sc = 0; while ((ptr = strsep(&sp, ","))) { TT.speeds[TT.sc] = encode(ptr); if (TT.speeds[TT.sc] < 0) perror_exit("Bad Speed"); if (++TT.sc > 10) perror_exit("Too many alternate speeds, Max is 10"); } } // Parse args and set TERM env. variable static void parse_arguments(void) { if (isdigit(**toys.optargs)) { get_speed(*toys.optargs); if (*++toys.optargs) TT.tty_name = xmsprintf("%s", *toys.optargs); } else { TT.tty_name = xmsprintf("%s", *toys.optargs); if (*++toys.optargs) get_speed(*toys.optargs); } if (*++toys.optargs) setenv("TERM", *toys.optargs, 1); } // Get controlling terminal and redirect stdio static void open_tty(void) { if (strcmp(TT.tty_name, "-")) { if (*(TT.tty_name) != '/') TT.tty_name = xmsprintf("/dev/%s", TT.tty_name); // Sends SIGHUP to all foreground process if Session leader don't die,Ignore sighandler_t sig = signal(SIGHUP, SIG_IGN); ioctl(0, TIOCNOTTY, 0); // Giveup if there is any controlling terminal signal(SIGHUP, sig); if (setsid() < 0) { // Seems we are session leader pid_t sid = getpid(); if(sid != getsid(0)) perror_exit("setsid"); } xclose(0); xopen(TT.tty_name, O_RDWR|O_NDELAY); fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read dup2(0, 1); dup2(0, 2); if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)"); if (!isatty(0)) perror_exit("/dev/%s:Not a character device", TT.tty_name); chown(TT.tty_name, 0, 0); // change ownership, Hope login will change this chmod(TT.tty_name, 0620); } else { // We already have opened TTY if (setsid() < 0) perror_msg("setsid:failed"); if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) perror_exit("opened tty don't have read/write permission"); } } // Intialise terminal settings static void termios_init(void) { if (tcgetattr(STDIN_FILENO, &TT.termios) < 0) perror_exit("tcgetattr"); // Flush input and output queues, important for modems! tcflush(STDIN_FILENO, TCIOFLUSH); TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD); #ifdef CRTSCTS if (toys.optflags & FLAG_h) TT.termios.c_cflag |= CRTSCTS; #endif if (toys.optflags & FLAG_L) TT.termios.c_cflag |= CLOCAL; TT.termios.c_cc[VTIME] = 0; TT.termios.c_cc[VMIN] = 1; TT.termios.c_oflag = OPOST|ONLCR; TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX; TT.termios.c_iflag = 0; // login will disable echo for passwd. TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE; TT.termios.c_cc[VINTR] = CTL('C'); TT.termios.c_cc[VQUIT] = CTL('\\'); TT.termios.c_cc[VEOF] = CTL('D'); TT.termios.c_cc[VEOL] = '\n'; TT.termios.c_cc[VKILL] = CTL('U'); TT.termios.c_cc[VERASE] = CERASE; TT.termios.c_iflag |= ICRNL|IXON|IXOFF; // set non-zero baud rate. Zero baud rate left it unchanged. if (TT.speeds[0] != B0) cfsetspeed(&TT.termios, TT.speeds[0]); if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr"); } // Get the baud rate from modems CONNECT mesage, Its of form <junk><BAUD><Junk> static void sense_baud(void) { int vmin; ssize_t size; char *ptr; speed_t speed; vmin = TT.termios.c_cc[VMIN]; // Store old TT.termios.c_cc[VMIN] = 0; // No block even queue is empty. if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr"); size = readall(STDIN_FILENO, TT.buff, sizeof(TT.buff)-1); if (size > 0) { for (ptr = TT.buff; ptr < TT.buff+size; ptr++) { if (isdigit(*ptr)) { speed = encode(ptr); if (speed > 0) cfsetspeed(&TT.termios,speed); break; } } } TT.termios.c_cc[VMIN] = vmin; //restore old value if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr"); } // Just prompt for login name void print_prompt(void) { char *hostname; struct utsname uts; uname(&uts); hostname = xstrdup(uts.nodename); fputs(hostname, stdout); fputs(" Login: ", stdout); fflush(NULL); free(hostname); hostname = NULL; } // Print /etc/isuue with taking care of each escape sequence void write_issue(char *file) { char buff[20] = {0,}; struct utsname u; uname(&u); int size, fd = open(TT.issue_str, O_RDONLY); if (fd < 0) return; while ((size = readall(fd, buff, 1)) > 0) { char *ch = buff; if (*ch == '\\' || *ch == '%') { if (readall(fd, buff, 1) <= 0) perror_exit("readall"); if (*ch == 's') fputs(u.sysname, stdout); if (*ch == 'n'|| *ch == 'h') fputs(u.nodename, stdout); if (*ch == 'r') fputs(u.release, stdout); if (*ch == 'm') fputs(u.machine, stdout); if (*ch == 'l') fputs(TT.tty_name, stdout); } else xputc(*ch); } } // Read login name and print prompt and Issue file. static int read_login_name(void) { tcflush(STDIN_FILENO, TCIFLUSH); // Flush pending speed switches int i = 0; while (1) { // Option -i will overide -f if (!(toys.optflags & FLAG_i)) write_issue(TT.issue_str); print_prompt(); TT.buff[0] = getchar(); if (!TT.buff[0] && TT.sc > 1) return 0; // Switch speed if (TT.buff[0] == '\n') continue; if (TT.buff[0] != '\n') if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1); while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++; TT.buff[i] = 0; break; } return 1; } // Put hostname entry in utmp file static void utmp_entry(void) { struct utmp entry; struct utmp *utp_ptr; pid_t pid = getpid(); utmpname(_PATH_UTMP); setutent(); // Starts from start while ((utp_ptr = getutent())) if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break; if (!utp_ptr) { entry.ut_type = LOGIN_PROCESS; entry.ut_pid = getpid(); xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE); time((time_t *)&entry.ut_time); xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE); if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg("Can't make utmp entry, Host length is greater than UT_HOSTSIZE(256)"); else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE); setutent(); pututline(&entry); return; } xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE); xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE); if (strlen(TT.host_str) > UT_HOSTSIZE) perror_msg("Can't make utmp entry,Host length is greater than UT_HOSTSIZE(256)"); else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE); time((time_t *)&entry.ut_time); setutent(); pututline(&entry); } void getty_main(void) { pid_t pid = getpid(); char *ptr[2] = {"/bin/login", NULL}; if (!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue"; if (toys.optflags & FLAG_l) ptr[0] = TT.login_str; parse_arguments(); open_tty(); termios_init(); tcsetpgrp(STDIN_FILENO, pid); if (toys.optflags & FLAG_H) utmp_entry(); if (toys.optflags & FLAG_I) writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str)); if (toys.optflags & FLAG_m) sense_baud(); if (toys.optflags & FLAG_t) alarm(TT.timeout); if (toys.optflags & FLAG_w) { char ch; while (readall(STDIN_FILENO, &ch, 1) != 1) if (ch == '\n' || ch == '\r') break; } if (!(toys.optflags & FLAG_n)) { int index = 1; // 0th we already set. while (1) { int l = read_login_name(); if (l) break; index = index % TT.sc; cfsetspeed(&TT.termios, TT.speeds[index]); // Select from multiple speeds //Necessary after cfsetspeed if (tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr"); } } if (toys.optflags & FLAG_n) execlp(*ptr, *ptr ,NULL); else execlp(*ptr, *ptr, TT.buff, NULL); perror_exit("error:%d",errno); // exec will return only if error }
_______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net