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

Reply via email to