Ignore:
Timestamp:
Jan 1, 2014, 12:47:38 AM (10 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.21.1
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/3.2/mindi-busybox/loginutils/getty.c

    r2725 r3232  
    11/* vi: set sw=4 ts=4: */
    2 /* agetty.c - another getty program for Linux. By W. Z. Venema 1989
     2/*
     3 * Based on agetty - another getty program for Linux. By W. Z. Venema 1989
    34 * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
    4  * This program is freely distributable. The entire man-page used to
    5  * be here. Now read the real man-page agetty.8 instead.
     5 * This program is freely distributable.
    66 *
    77 * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
    88 *
    99 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
    10  * - added Native Language Support
     10 * - Added Native Language Support
    1111 *
    1212 * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
    13  * - enable hardware flow control before displaying /etc/issue
     13 * - Enabled hardware flow control before displaying /etc/issue
     14 *
     15 * 2011-01 Venys Vlasenko
     16 * - Removed parity detection code. It can't work reliably:
     17 * if all chars received have bit 7 cleared and odd (or even) parity,
     18 * it is impossible to determine whether other side is 8-bit,no-parity
     19 * or 7-bit,odd(even)-parity. It also interferes with non-ASCII usernames.
     20 * - From now on, we assume that parity is correctly set.
    1421 *
    1522 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
     
    1825#include "libbb.h"
    1926#include <syslog.h>
    20 
    21 #if ENABLE_FEATURE_UTMP
    22 # include <utmp.h> /* LOGIN_PROCESS */
    23 #endif
    24 
    2527#ifndef IUCLC
    2628# define IUCLC 0
    2729#endif
    2830
    29 /*
    30  * Some heuristics to find out what environment we are in: if it is not
    31  * System V, assume it is SunOS 4.
    32  */
    33 #ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */
    34 #include <sys/utsname.h>
    35 #else /* if !sysV style, wtmp/utmp code is off */
    36 #undef ENABLE_FEATURE_UTMP
    37 #undef ENABLE_FEATURE_WTMP
    38 #define ENABLE_FEATURE_UTMP 0
    39 #define ENABLE_FEATURE_WTMP 0
    40 #endif  /* LOGIN_PROCESS */
     31#ifndef LOGIN_PROCESS
     32# undef ENABLE_FEATURE_UTMP
     33# undef ENABLE_FEATURE_WTMP
     34# define ENABLE_FEATURE_UTMP 0
     35# define ENABLE_FEATURE_WTMP 0
     36#endif
     37
     38
     39/* The following is used for understandable diagnostics */
     40#ifdef DEBUGGING
     41static FILE *dbf;
     42# define DEBUGTERM "/dev/ttyp0"
     43# define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
     44#else
     45# define debug(...) ((void)0)
     46#endif
     47
    4148
    4249/*
     
    4754 * and for line editing at the same time.
    4855 */
    49 
    50 /* I doubt there are systems which still need this */
    51 #undef HANDLE_ALLCAPS
    52 #undef ANCIENT_BS_KILL_CHARS
    53 
     56#undef  _PATH_LOGIN
    5457#define _PATH_LOGIN "/bin/login"
    5558
    56 /* If ISSUE is not defined, getty will never display the contents of the
     59/* Displayed before the login prompt.
     60 * If ISSUE is not defined, getty will never display the contents of the
    5761 * /etc/issue file. You will not want to spit out large "issue" files at the
    5862 * wrong baud rate.
    5963 */
    60 #define ISSUE "/etc/issue"              /* displayed before the login prompt */
    61 
    62 /* Some shorthands for control characters. */
    63 #define CTL(x)          ((x) ^ 0100)    /* Assumes ASCII dialect */
    64 #define CR              CTL('M')        /* carriage return */
    65 #define NL              CTL('J')        /* line feed */
    66 #define BS              CTL('H')        /* back space */
    67 #define DEL             CTL('?')        /* delete */
    68 
    69 /* Defaults for line-editing etc. characters; you may want to change this. */
    70 #define DEF_ERASE       DEL             /* default erase character */
    71 #define DEF_INTR        CTL('C')        /* default interrupt character */
    72 #define DEF_QUIT        CTL('\\')       /* default quit char */
    73 #define DEF_KILL        CTL('U')        /* default kill char */
    74 #define DEF_EOF         CTL('D')        /* default EOF char */
    75 #define DEF_EOL         '\n'
    76 #define DEF_SWITCH      0               /* default switch char */
     64#define ISSUE "/etc/issue"
     65
     66/* Macro to build Ctrl-LETTER. Assumes ASCII dialect */
     67#define CTL(x)          ((x) ^ 0100)
    7768
    7869/*
    79  * When multiple baud rates are specified on the command line, the first one
    80  * we will try is the first one specified.
     70 * When multiple baud rates are specified on the command line,
     71 * the first one we will try is the first one specified.
    8172 */
    8273#define MAX_SPEED       10              /* max. nr. of baud rates */
    8374
    84 /* Storage for command-line options. */
    85 struct options {
    86     int flags;                      /* toggle switches, see below */
    87     unsigned timeout;               /* time-out period */
     75struct globals {
     76    unsigned timeout;
    8877    const char *login;              /* login program */
    89     const char *tty;                /* name of tty */
    90     const char *initstring;         /* modem init string */
     78    const char *fakehost;
     79    const char *tty_name;
     80    char *initstring;               /* modem init string */
    9181    const char *issue;              /* alternative issue file */
    9282    int numspeed;                   /* number of baud rates to try */
    9383    int speeds[MAX_SPEED];          /* baud rates to be tried */
     84    unsigned char eol;              /* end-of-line char seen (CR or NL) */
     85    struct termios tty_attrs;
     86    char line_buf[128];
    9487};
    9588
    96 /* Storage for things detected while the login name was read. */
    97 struct chardata {
    98     unsigned char erase;    /* erase character */
    99     unsigned char kill;     /* kill character */
    100     unsigned char eol;      /* end-of-line character */
    101     unsigned char parity;   /* what parity did we see */
    102     /* (parity & 1): saw odd parity char with 7th bit set */
    103     /* (parity & 2): saw even parity char with 7th bit set */
    104     /* parity == 0: probably 7-bit, space parity? */
    105     /* parity == 1: probably 7-bit, odd parity? */
    106     /* parity == 2: probably 7-bit, even parity? */
    107     /* parity == 3: definitely 8 bit, no parity! */
    108     /* Hmm... with any value of "parity" 8 bit, no parity is possible */
    109 #ifdef HANDLE_ALLCAPS
    110     unsigned char capslock; /* upper case without lower case */
    111 #endif
    112 };
    113 
    114 
    115 /* Initial values for the above. */
    116 static const struct chardata init_chardata = {
    117     DEF_ERASE,                              /* default erase character */
    118     DEF_KILL,                               /* default kill character */
    119     13,                                     /* default eol char */
    120     0,                                      /* space parity */
    121 #ifdef HANDLE_ALLCAPS
    122     0,                                      /* no capslock */
    123 #endif
    124 };
     89#define G (*ptr_to_globals)
     90#define INIT_G() do { \
     91    SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
     92} while (0)
     93
     94//usage:#define getty_trivial_usage
     95//usage:       "[OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]"
     96//usage:#define getty_full_usage "\n\n"
     97//usage:       "Open TTY, prompt for login name, then invoke /bin/login\n"
     98//usage:     "\n    -h      Enable hardware RTS/CTS flow control"
     99//usage:     "\n    -L      Set CLOCAL (ignore Carrier Detect state)"
     100//usage:     "\n    -m      Get baud rate from modem's CONNECT status message"
     101//usage:     "\n    -n      Don't prompt for login name"
     102//usage:     "\n    -w      Wait for CR or LF before sending /etc/issue"
     103//usage:     "\n    -i      Don't display /etc/issue"
     104//usage:     "\n    -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue"
     105//usage:     "\n    -l LOGIN    Invoke LOGIN instead of /bin/login"
     106//usage:     "\n    -t SEC      Terminate after SEC if no login name is read"
     107//usage:     "\n    -I INITSTR  Send INITSTR before anything else"
     108//usage:     "\n    -H HOST     Log HOST into the utmp file as the hostname"
     109//usage:     "\n"
     110//usage:     "\nBAUD_RATE of 0 leaves it unchanged"
    125111
    126112static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
    127 #define F_INITSTRING    (1 << 0)        /* -I initstring is set */
    128 #define F_LOCAL         (1 << 1)        /* -L force local */
    129 #define F_FAKEHOST      (1 << 2)        /* -H fake hostname */
    130 #define F_CUSTISSUE     (1 << 3)        /* -f give alternative issue file */
    131 #define F_RTSCTS        (1 << 4)        /* -h enable RTS/CTS flow control */
    132 #define F_ISSUE         (1 << 5)        /* -i display /etc/issue */
    133 #define F_LOGIN         (1 << 6)        /* -l non-default login program */
    134 #define F_PARSE         (1 << 7)        /* -m process modem status messages */
    135 #define F_TIMEOUT       (1 << 8)        /* -t time out */
    136 #define F_WAITCRLF      (1 << 9)        /* -w wait for CR or LF */
    137 #define F_NOPROMPT      (1 << 10)       /* -n don't ask for login name */
    138 
    139 
    140 #define line_buf bb_common_bufsiz1
    141 
    142 /* The following is used for understandable diagnostics. */
    143 #ifdef DEBUGGING
    144 static FILE *dbf;
    145 #define DEBUGTERM "/dev/ttyp0"
    146 #define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
    147 #else
    148 #define debug(...) ((void)0)
    149 #endif
    150 
    151 
    152 /* bcode - convert speed string to speed code; return <= 0 on failure */
     113#define F_INITSTRING    (1 << 0)   /* -I */
     114#define F_LOCAL         (1 << 1)   /* -L */
     115#define F_FAKEHOST      (1 << 2)   /* -H */
     116#define F_CUSTISSUE     (1 << 3)   /* -f */
     117#define F_RTSCTS        (1 << 4)   /* -h */
     118#define F_NOISSUE       (1 << 5)   /* -i */
     119#define F_LOGIN         (1 << 6)   /* -l */
     120#define F_PARSE         (1 << 7)   /* -m */
     121#define F_TIMEOUT       (1 << 8)   /* -t */
     122#define F_WAITCRLF      (1 << 9)   /* -w */
     123#define F_NOPROMPT      (1 << 10)  /* -n */
     124
     125
     126/* convert speed string to speed code; return <= 0 on failure */
    153127static int bcode(const char *s)
    154128{
     
    159133}
    160134
    161 /* parse_speeds - parse alternate baud rates */
    162 static void parse_speeds(struct options *op, char *arg)
     135/* parse alternate baud rates */
     136static void parse_speeds(char *arg)
    163137{
    164138    char *cp;
     
    167141    debug("entered parse_speeds\n");
    168142    while ((cp = strsep(&arg, ",")) != NULL) {
    169         op->speeds[op->numspeed] = bcode(cp);
    170         if (op->speeds[op->numspeed] < 0)
     143        G.speeds[G.numspeed] = bcode(cp);
     144        if (G.speeds[G.numspeed] < 0)
    171145            bb_error_msg_and_die("bad speed: %s", cp);
    172146        /* note: arg "0" turns into speed B0 */
    173         op->numspeed++;
    174         if (op->numspeed > MAX_SPEED)
     147        G.numspeed++;
     148        if (G.numspeed > MAX_SPEED)
    175149            bb_error_msg_and_die("too many alternate speeds");
    176150    }
     
    178152}
    179153
    180 /* parse_args - parse command-line arguments */
    181 static void parse_args(char **argv, struct options *op, char **fakehost_p)
     154/* parse command-line arguments */
     155static void parse_args(char **argv)
    182156{
    183157    char *ts;
     158    int flags;
    184159
    185160    opt_complementary = "-2:t+"; /* at least 2 args; -t N */
    186     op->flags = getopt32(argv, opt_string,
    187         &(op->initstring), fakehost_p, &(op->issue),
    188         &(op->login), &op->timeout);
     161    flags = getopt32(argv, opt_string,
     162        &G.initstring, &G.fakehost, &G.issue,
     163        &G.login, &G.timeout
     164    );
     165    if (flags & F_INITSTRING) {
     166        G.initstring = xstrdup(G.initstring);
     167        /* decode \ddd octal codes into chars */
     168        strcpy_and_process_escape_sequences(G.initstring, G.initstring);
     169    }
    189170    argv += optind;
    190     if (op->flags & F_INITSTRING) {
    191         op->initstring = xstrdup(op->initstring);
    192         /* decode \ddd octal codes into chars */
    193         strcpy_and_process_escape_sequences((char*)op->initstring, op->initstring);
    194     }
    195     op->flags ^= F_ISSUE;           /* invert flag "show /etc/issue" */
    196171    debug("after getopt\n");
    197172
    198     /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
    199     op->tty = argv[0];      /* tty name */
    200     ts = argv[1];           /* baud rate(s) */
     173    /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
     174    G.tty_name = argv[0];
     175    ts = argv[1];            /* baud rate(s) */
    201176    if (isdigit(argv[0][0])) {
    202         /* a number first, assume it's a speed (BSD style) */
    203         op->tty = ts;   /* tty name is in argv[1] */
    204         ts = argv[0];   /* baud rate(s) */
    205     }
    206     parse_speeds(op, ts);
    207     applet_name = xasprintf("getty: %s", op->tty);
     177        /* A number first, assume it's a speed (BSD style) */
     178        G.tty_name = ts; /* tty name is in argv[1] */
     179        ts = argv[0];    /* baud rate(s) */
     180    }
     181    parse_speeds(ts);
    208182
    209183    if (argv[2])
     
    213187}
    214188
    215 /* open_tty - set up tty as standard { input, output, error } */
    216 static void open_tty(const char *tty)
    217 {
    218     /* Set up new standard input, unless we are given an already opened port. */
    219     if (NOT_LONE_DASH(tty)) {
    220 //      struct stat st;
    221 //      int cur_dir_fd;
    222 //      int fd;
    223 
    224         /* Sanity checks... */
    225 //      cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
    226 //      xchdir("/dev");
    227 //      xstat(tty, &st);
    228 //      if (!S_ISCHR(st.st_mode))
    229 //          bb_error_msg_and_die("not a character device");
    230 
    231         if (tty[0] != '/')
    232             tty = xasprintf("/dev/%s", tty); /* will leak it */
    233 
    234         /* Open the tty as standard input. */
     189/* set up tty as standard input, output, error */
     190static void open_tty(void)
     191{
     192    /* Set up new standard input, unless we are given an already opened port */
     193    if (NOT_LONE_DASH(G.tty_name)) {
     194        if (G.tty_name[0] != '/')
     195            G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */
     196
     197        /* Open the tty as standard input */
    235198        debug("open(2)\n");
    236199        close(0);
    237         /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
    238 
    239 //      /* Restore current directory */
    240 //      fchdir(cur_dir_fd);
    241 
    242         /* Open the tty as standard input, continued */
    243 //      xmove_fd(fd, 0);
    244 //      /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
    245 //      while (fd > 2)
    246 //          close(fd--);
    247 
    248         /* Set proper protections and ownership. */
     200        xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */
     201
     202        /* Set proper protections and ownership */
    249203        fchown(0, 0, 0);        /* 0:0 */
    250204        fchmod(0, 0620);        /* crw--w---- */
    251205    } else {
     206        char *n;
    252207        /*
    253          * Standard input should already be connected to an open port. Make
    254          * sure it is open for read/write.
     208         * Standard input should already be connected to an open port.
     209         * Make sure it is open for read/write.
    255210         */
    256         if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
     211        if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
    257212            bb_error_msg_and_die("stdin is not open for read/write");
    258     }
    259 }
    260 
    261 /* termios_init - initialize termios settings */
    262 static void termios_init(struct termios *tp, int speed, struct options *op)
    263 {
    264     speed_t ispeed, ospeed;
    265     /*
    266      * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
     213
     214        /* Try to get real tty name instead of "-" */
     215        n = xmalloc_ttyname(0);
     216        if (n)
     217            G.tty_name = n;
     218    }
     219    applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name));
     220}
     221
     222static void set_tty_attrs(void)
     223{
     224    if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0)
     225        bb_perror_msg_and_die("tcsetattr");
     226}
     227
     228/* We manipulate tty_attrs this way:
     229 * - first, we read existing tty_attrs
     230 * - init_tty_attrs modifies some parts and sets it
     231 * - auto_baud and/or BREAK processing can set different speed and set tty attrs
     232 * - finalize_tty_attrs again modifies some parts and sets tty attrs before
     233 *   execing login
     234 */
     235static void init_tty_attrs(int speed)
     236{
     237    /* Try to drain output buffer, with 5 sec timeout.
     238     * Added on request from users of ~600 baud serial interface
     239     * with biggish buffer on a 90MHz CPU.
     240     * They were losing hundreds of bytes of buffered output
     241     * on tcflush.
     242     */
     243    signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo);
     244    alarm(5);
     245    tcdrain(STDIN_FILENO);
     246    alarm(0);
     247
     248    /* Flush input and output queues, important for modems! */
     249    tcflush(STDIN_FILENO, TCIOFLUSH);
     250
     251    /* Set speed if it wasn't specified as "0" on command line */
     252    if (speed != B0)
     253        cfsetspeed(&G.tty_attrs, speed);
     254
     255    /* Initial settings: 8-bit characters, raw mode, blocking i/o.
    267256     * Special characters are set after we have read the login name; all
    268      * reads will be done in raw mode anyway. Errors will be dealt with
    269      * later on.
    270      */
    271     /* flush input and output queues, important for modems! */
    272     tcflush(0, TCIOFLUSH);
    273     ispeed = ospeed = speed;
    274     if (speed == B0) {
    275         /* Speed was specified as "0" on command line.
    276          * Just leave it unchanged */
    277         ispeed = cfgetispeed(tp);
    278         ospeed = cfgetospeed(tp);
    279     }
    280     tp->c_cflag = CS8 | HUPCL | CREAD;
    281     if (op->flags & F_LOCAL)
    282         tp->c_cflag |= CLOCAL;
    283     cfsetispeed(tp, ispeed);
    284     cfsetospeed(tp, ospeed);
    285 
    286     tp->c_iflag = tp->c_lflag = 0;
    287     tp->c_oflag = OPOST | ONLCR;
    288     tp->c_cc[VMIN] = 1;
    289     tp->c_cc[VTIME] = 0;
     257     * reads will be done in raw mode anyway.
     258     */
     259    /* Clear all bits except: */
     260    G.tty_attrs.c_cflag &= (0
     261        /* 2 stop bits (1 otherwise)
     262         * Enable parity bit (both on input and output)
     263         * Odd parity (else even)
     264         */
     265        | CSTOPB | PARENB | PARODD
     266#ifdef CMSPAR
     267        | CMSPAR  /* mark or space parity */
     268#endif
     269#ifdef CBAUD
     270        | CBAUD   /* (output) baud rate */
     271#endif
     272#ifdef CBAUDEX
     273        | CBAUDEX /* (output) baud rate */
     274#endif
     275#ifdef CIBAUD
     276        | CIBAUD   /* input baud rate */
     277#endif
     278    );
     279    /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
     280    G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD;
     281    if (option_mask32 & F_LOCAL) {
     282        /* ignore Carrier Detect pin:
     283         * opens don't block when CD is low,
     284         * losing CD doesn't hang up processes whose ctty is this tty
     285         */
     286        G.tty_attrs.c_cflag |= CLOCAL;
     287    }
     288#ifdef CRTSCTS
     289    if (option_mask32 & F_RTSCTS)
     290        G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
     291#endif
     292    G.tty_attrs.c_iflag = 0;
     293    G.tty_attrs.c_lflag = 0;
     294    /* non-raw output; add CR to each NL */
     295    G.tty_attrs.c_oflag = OPOST | ONLCR;
     296
     297    /* reads would block only if < 1 char is available */
     298    G.tty_attrs.c_cc[VMIN] = 1;
     299    /* no timeout (reads block forever) */
     300    G.tty_attrs.c_cc[VTIME] = 0;
    290301#ifdef __linux__
    291     tp->c_line = 0;
    292 #endif
    293 
    294     /* Optionally enable hardware flow control */
    295 #ifdef CRTSCTS
    296     if (op->flags & F_RTSCTS)
    297         tp->c_cflag |= CRTSCTS;
    298 #endif
    299 
    300     tcsetattr_stdin_TCSANOW(tp);
     302    G.tty_attrs.c_line = 0;
     303#endif
     304
     305    set_tty_attrs();
    301306
    302307    debug("term_io 2\n");
    303308}
    304309
    305 /* auto_baud - extract baud rate from modem status message */
    306 static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
    307 {
    308     int speed;
    309     int vmin;
    310     unsigned iflag;
    311     char *bp;
     310static void finalize_tty_attrs(void)
     311{
     312    /* software flow control on output (stop sending if XOFF is recvd);
     313     * and on input (send XOFF when buffer is full)
     314     */
     315    G.tty_attrs.c_iflag |= IXON | IXOFF;
     316    if (G.eol == '\r') {
     317        G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */
     318    }
     319    /* Other bits in c_iflag:
     320     * IXANY   Any recvd char enables output (any char is also a XON)
     321     * INPCK   Enable parity check
     322     * IGNPAR  Ignore parity errors (drop bad bytes)
     323     * PARMRK  Mark parity errors with 0xff, 0x00 prefix
     324     *         (else bad byte is received as 0x00)
     325     * ISTRIP  Strip parity bit
     326     * IGNBRK  Ignore break condition
     327     * BRKINT  Send SIGINT on break - maybe set this?
     328     * INLCR   Map NL to CR
     329     * IGNCR   Ignore CR
     330     * ICRNL   Map CR to NL
     331     * IUCLC   Map uppercase to lowercase
     332     * IMAXBEL Echo BEL on input line too long
     333     * IUTF8   Appears to affect tty's idea of char widths,
     334     *         observed to improve backspacing through Unicode chars
     335     */
     336
     337    /* line buffered input (NL or EOL or EOF chars end a line);
     338     * recognize INT/QUIT/SUSP chars;
     339     * echo input chars;
     340     * echo BS-SP-BS on erase character;
     341     * echo kill char specially, not as ^c (ECHOKE controls how exactly);
     342     * erase all input via BS-SP-BS on kill char (else go to next line)
     343     */
     344    G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
     345    /* Other bits in c_lflag:
     346     * XCASE   Map uppercase to \lowercase [tried, doesn't work]
     347     * ECHONL  Echo NL even if ECHO is not set
     348     * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
     349     * ECHOPRT On erase, echo erased chars
     350     *         [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
     351     * NOFLSH  Don't flush input buffer after interrupt or quit chars
     352     * IEXTEN  Enable extended functions (??)
     353     *         [glibc says it enables c_cc[LNEXT] "enter literal char"
     354     *         and c_cc[VDISCARD] "toggle discard buffered output" chars]
     355     * FLUSHO  Output being flushed (c_cc[VDISCARD] is in effect)
     356     * PENDIN  Retype pending input at next read or input char
     357     *         (c_cc[VREPRINT] is being processed)
     358     * TOSTOP  Send SIGTTOU for background output
     359     *         (why "stty sane" unsets this bit?)
     360     */
     361
     362    G.tty_attrs.c_cc[VINTR] = CTL('C');
     363    G.tty_attrs.c_cc[VQUIT] = CTL('\\');
     364    G.tty_attrs.c_cc[VEOF] = CTL('D');
     365    G.tty_attrs.c_cc[VEOL] = '\n';
     366#ifdef VSWTC
     367    G.tty_attrs.c_cc[VSWTC] = 0;
     368#endif
     369#ifdef VSWTCH
     370    G.tty_attrs.c_cc[VSWTCH] = 0;
     371#endif
     372    G.tty_attrs.c_cc[VKILL] = CTL('U');
     373    /* Other control chars:
     374     * VEOL2
     375     * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname
     376     * VREPRINT - reprint current input buffer
     377     * VLNEXT, VDISCARD, VSTATUS
     378     * VSUSP, VDSUSP - send (delayed) SIGTSTP
     379     * VSTART, VSTOP - chars used for IXON/IXOFF
     380     */
     381
     382    set_tty_attrs();
     383
     384    /* Now the newline character should be properly written */
     385    full_write(STDOUT_FILENO, "\n", 1);
     386}
     387
     388/* extract baud rate from modem status message */
     389static void auto_baud(void)
     390{
    312391    int nread;
    313392
     
    327406     */
    328407
    329     /*
    330      * Use 7-bit characters, don't block if input queue is empty. Errors will
    331      * be dealt with later on.
    332      */
    333     iflag = tp->c_iflag;
    334     tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
    335     vmin = tp->c_cc[VMIN];
    336     tp->c_cc[VMIN] = 0;             /* don't block if queue empty */
    337     tcsetattr_stdin_TCSANOW(tp);
     408    G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
     409    set_tty_attrs();
    338410
    339411    /*
     
    342414     */
    343415    sleep(1);
    344     nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
     416    nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1);
    345417    if (nread > 0) {
    346         buf[nread] = '\0';
    347         for (bp = buf; bp < buf + nread; bp++) {
     418        int speed;
     419        char *bp;
     420        G.line_buf[nread] = '\0';
     421        for (bp = G.line_buf; bp < G.line_buf + nread; bp++) {
    348422            if (isdigit(*bp)) {
    349423                speed = bcode(bp);
    350424                if (speed > 0)
    351                     cfsetspeed(tp, speed);
     425                    cfsetspeed(&G.tty_attrs, speed);
    352426                break;
    353427            }
     
    355429    }
    356430
    357     /* Restore terminal settings. Errors will be dealt with later on. */
    358     tp->c_iflag = iflag;
    359     tp->c_cc[VMIN] = vmin;
    360     tcsetattr_stdin_TCSANOW(tp);
    361 }
    362 
    363 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
    364 static void do_prompt(struct options *op)
    365 {
     431    /* Restore terminal settings */
     432    G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */
     433    set_tty_attrs();
     434}
     435
     436/* get user name, establish parity, speed, erase, kill, eol;
     437 * return NULL on BREAK, logname on success
     438 */
     439static char *get_logname(void)
     440{
     441    char *bp;
     442    char c;
     443
     444    /* Flush pending input (esp. after parsing or switching the baud rate) */
     445    usleep(100*1000); /* 0.1 sec */
     446    tcflush(STDIN_FILENO, TCIFLUSH);
     447
     448    /* Prompt for and read a login name */
     449    do {
     450        /* Write issue file and prompt */
    366451#ifdef ISSUE
    367     print_login_issue(op->issue, op->tty);
    368 #endif
    369     print_login_prompt();
    370 }
    371 
    372 #ifdef HANDLE_ALLCAPS
    373 /* all_is_upcase - string contains upper case without lower case */
    374 /* returns 1 if true, 0 if false */
    375 static int all_is_upcase(const char *s)
    376 {
    377     while (*s)
    378         if (islower(*s++))
    379             return 0;
    380     return 1;
    381 }
    382 #endif
    383 
    384 /* get_logname - get user name, establish parity, speed, erase, kill, eol;
    385  * return NULL on BREAK, logname on success */
    386 static char *get_logname(char *logname, unsigned size_logname,
    387         struct options *op, struct chardata *cp)
    388 {
    389     char *bp;
    390     char c;                         /* input character, full eight bits */
    391     char ascval;                    /* low 7 bits of input character */
    392     int bits;                       /* # of "1" bits per character */
    393     int mask;                       /* mask with 1 bit up */
    394     static const char erase[][3] = {/* backspace-space-backspace */
    395         "\010\040\010",                 /* space parity */
    396         "\010\040\010",                 /* odd parity */
    397         "\210\240\210",                 /* even parity */
    398         "\010\040\010",                 /* 8 bit no parity */
    399     };
    400 
    401     /* NB: *cp is pre-initialized with init_chardata */
    402 
    403     /* Flush pending input (esp. after parsing or switching the baud rate). */
    404     sleep(1);
    405     tcflush(0, TCIOFLUSH);
    406 
    407     /* Prompt for and read a login name. */
    408     logname[0] = '\0';
    409     while (!logname[0]) {
    410         /* Write issue file and prompt, with "parity" bit == 0. */
    411         do_prompt(op);
    412 
    413         /* Read name, watch for break, parity, erase, kill, end-of-line. */
    414         bp = logname;
    415         cp->eol = '\0';
    416         while (cp->eol == '\0') {
    417 
    418             /* Do not report trivial EINTR/EIO errors. */
     452        if (!(option_mask32 & F_NOISSUE))
     453            print_login_issue(G.issue, G.tty_name);
     454#endif
     455        print_login_prompt();
     456
     457        /* Read name, watch for break, erase, kill, end-of-line */
     458        bp = G.line_buf;
     459        while (1) {
     460            /* Do not report trivial EINTR/EIO errors */
    419461            errno = EINTR; /* make read of 0 bytes be silent too */
    420462            if (read(STDIN_FILENO, &c, 1) < 1) {
     463                finalize_tty_attrs();
    421464                if (errno == EINTR || errno == EIO)
    422465                    exit(EXIT_SUCCESS);
     
    424467            }
    425468
    426             /* BREAK. If we have speeds to try,
    427              * return NULL (will switch speeds and return here) */
    428             if (c == '\0' && op->numspeed > 1)
    429                 return NULL;
    430 
    431             /* Do parity bit handling. */
    432             if (!(op->flags & F_LOCAL) && (c & 0x80)) {       /* "parity" bit on? */
    433                 bits = 1;
    434                 mask = 1;
    435                 while (mask & 0x7f) {
    436                     if (mask & c)
    437                         bits++; /* count "1" bits */
    438                     mask <<= 1;
    439                 }
    440                 /* ... |= 2 - even, 1 - odd */
    441                 cp->parity |= 2 - (bits & 1);
    442             }
    443 
    444             /* Do erase, kill and end-of-line processing. */
    445             ascval = c & 0x7f;
    446             switch (ascval) {
    447             case CR:
    448             case NL:
    449                 *bp = '\0';             /* terminate logname */
    450                 cp->eol = ascval;       /* set end-of-line char */
    451                 break;
    452             case BS:
    453             case DEL:
    454 #ifdef ANCIENT_BS_KILL_CHARS
    455             case '#':
    456 #endif
    457                 cp->erase = ascval;     /* set erase character */
    458                 if (bp > logname) {
    459                     full_write(STDOUT_FILENO, erase[cp->parity], 3);
     469            switch (c) {
     470            case '\r':
     471            case '\n':
     472                *bp = '\0';
     473                G.eol = c;
     474                goto got_logname;
     475            case CTL('H'):
     476            case 0x7f:
     477                G.tty_attrs.c_cc[VERASE] = c;
     478                if (bp > G.line_buf) {
     479                    full_write(STDOUT_FILENO, "\010 \010", 3);
    460480                    bp--;
    461481                }
    462482                break;
    463483            case CTL('U'):
    464 #ifdef ANCIENT_BS_KILL_CHARS
    465             case '@':
    466 #endif
    467                 cp->kill = ascval;      /* set kill character */
    468                 while (bp > logname) {
    469                     full_write(STDOUT_FILENO, erase[cp->parity], 3);
     484                while (bp > G.line_buf) {
     485                    full_write(STDOUT_FILENO, "\010 \010", 3);
    470486                    bp--;
    471487                }
    472488                break;
     489            case CTL('C'):
    473490            case CTL('D'):
     491                finalize_tty_attrs();
    474492                exit(EXIT_SUCCESS);
     493            case '\0':
     494                /* BREAK. If we have speeds to try,
     495                 * return NULL (will switch speeds and return here) */
     496                if (G.numspeed > 1)
     497                    return NULL;
     498                /* fall through and ignore it */
    475499            default:
    476                 if (ascval < ' ') {
     500                if ((unsigned char)c < ' ') {
    477501                    /* ignore garbage characters */
    478                 } else if ((int)(bp - logname) >= size_logname - 1) {
    479                     bb_error_msg_and_die("input overrun");
    480                 } else {
    481                     full_write(STDOUT_FILENO, &c, 1); /* echo the character */
    482                     *bp++ = ascval; /* and store it */
     502                } else if ((int)(bp - G.line_buf) < sizeof(G.line_buf) - 1) {
     503                    /* echo and store the character */
     504                    full_write(STDOUT_FILENO, &c, 1);
     505                    *bp++ = c;
    483506                }
    484507                break;
    485508            }
    486         }
    487     }
    488     /* Handle names with upper case and no lower case. */
    489 
    490 #ifdef HANDLE_ALLCAPS
    491     cp->capslock = all_is_upcase(logname);
    492     if (cp->capslock) {
    493         for (bp = logname; *bp; bp++)
    494             if (isupper(*bp))
    495                 *bp = tolower(*bp);     /* map name to lower case */
    496     }
    497 #endif
    498     return logname;
    499 }
    500 
    501 /* termios_final - set the final tty mode bits */
    502 static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
    503 {
    504     /* General terminal-independent stuff. */
    505     tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
    506     tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
    507     /* no longer| ECHOCTL | ECHOPRT */
    508     tp->c_oflag |= OPOST;
    509     /* tp->c_cflag = 0; */
    510     tp->c_cc[VINTR] = DEF_INTR;     /* default interrupt */
    511     tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
    512     tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
    513     tp->c_cc[VEOL] = DEF_EOL;
    514 #ifdef VSWTC
    515     tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
    516 #endif
    517 
    518     /* Account for special characters seen in input. */
    519     if (cp->eol == CR) {
    520         tp->c_iflag |= ICRNL;   /* map CR in input to NL */
    521         tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */
    522     }
    523     tp->c_cc[VERASE] = cp->erase;   /* set erase character */
    524     tp->c_cc[VKILL] = cp->kill;     /* set kill character */
    525 
    526     /* Account for the presence or absence of parity bits in input. */
    527     switch (cp->parity) {
    528     case 0:                                 /* space (always 0) parity */
    529 // I bet most people go here - they use only 7-bit chars in usernames....
    530         break;
    531     case 1:                                 /* odd parity */
    532         tp->c_cflag |= PARODD;
    533         /* FALLTHROUGH */
    534     case 2:                                 /* even parity */
    535         tp->c_cflag |= PARENB;
    536         tp->c_iflag |= INPCK | ISTRIP;
    537         /* FALLTHROUGH */
    538     case (1 | 2):                           /* no parity bit */
    539         tp->c_cflag &= ~CSIZE;
    540         tp->c_cflag |= CS7;
    541 // FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
    542 // it's probably some umlauts etc, but definitely NOT 7-bit!!!
    543 // Entire parity detection madness here just begs for deletion...
    544         break;
    545     }
    546 
    547     /* Account for upper case without lower case. */
    548 #ifdef HANDLE_ALLCAPS
    549     if (cp->capslock) {
    550         tp->c_iflag |= IUCLC;
    551         tp->c_lflag |= XCASE;
    552         tp->c_oflag |= OLCUC;
    553     }
    554 #endif
    555     /* Optionally enable hardware flow control */
    556 #ifdef CRTSCTS
    557     if (op->flags & F_RTSCTS)
    558         tp->c_cflag |= CRTSCTS;
    559 #endif
    560 
    561     /* Finally, make the new settings effective */
    562     if (tcsetattr_stdin_TCSANOW(tp) < 0)
    563         bb_perror_msg_and_die("tcsetattr");
     509        } /* end of get char loop */
     510 got_logname: ;
     511    } while (G.line_buf[0] == '\0');  /* while logname is empty */
     512
     513    return G.line_buf;
     514}
     515
     516static void alarm_handler(int sig UNUSED_PARAM)
     517{
     518    finalize_tty_attrs();
     519    _exit(EXIT_SUCCESS);
    564520}
    565521
     
    568524{
    569525    int n;
    570     pid_t pid;
    571     char *fakehost = NULL;          /* Fake hostname for ut_host */
    572     char *logname;                  /* login name, given to /bin/login */
    573     /* Merging these into "struct local" may _seem_ to reduce
    574      * parameter passing, but today's gcc will inline
    575      * statics which are called once anyway, so don't do that */
    576     struct chardata chardata;       /* set by get_logname() */
    577     struct termios termios;         /* terminal mode bits */
    578     struct options options;
    579 
    580     chardata = init_chardata;
    581 
    582     memset(&options, 0, sizeof(options));
    583     options.login = _PATH_LOGIN;    /* default login program */
    584     options.tty = "tty1";           /* default tty line */
    585     options.initstring = "";        /* modem init string */
     526    pid_t pid, tsid;
     527    char *logname;
     528
     529    INIT_G();
     530    G.login = _PATH_LOGIN;    /* default login program */
    586531#ifdef ISSUE
    587     options.issue = ISSUE;          /* default issue file */
    588 #endif
    589 
    590     /* Parse command-line arguments. */
    591     parse_args(argv, &options, &fakehost);
    592 
    593     logmode = LOGMODE_NONE;
    594 
    595     /* Create new session, lose controlling tty, if any */
    596     /* docs/ctty.htm says:
    597      * "This is allowed only when the current process
    598      *  is not a process group leader" - is this a problem? */
    599     setsid();
    600     /* close stdio, and stray descriptors, just in case */
     532    G.issue = ISSUE;          /* default issue file */
     533#endif
     534    G.eol = '\r';
     535
     536    /* Parse command-line arguments */
     537    parse_args(argv);
     538
     539    /* Create new session and pgrp, lose controlling tty */
     540    pid = setsid();  /* this also gives us our pid :) */
     541    if (pid < 0) {
     542        int fd;
     543        /* :(
     544         * docs/ctty.htm says:
     545         * "This is allowed only when the current process
     546         *  is not a process group leader".
     547         * Thus, setsid() will fail if we _already_ are
     548         * a session leader - which is quite possible for getty!
     549         */
     550        pid = getpid();
     551        if (getsid(0) != pid) {
     552            //for debugging:
     553            //bb_perror_msg_and_die("setsid failed:"
     554            //  " pid %d ppid %d"
     555            //  " sid %d pgid %d",
     556            //  pid, getppid(),
     557            //  getsid(0), getpgid(0));
     558            bb_perror_msg_and_die("setsid");
     559        }
     560        /* Looks like we are already a session leader.
     561         * In this case (setsid failed) we may still have ctty,
     562         * and it may be different from tty we need to control!
     563         * If we still have ctty, on Linux ioctl(TIOCSCTTY)
     564         * (which we are going to use a bit later) always fails -
     565         * even if we try to take ctty which is already ours!
     566         * Try to drop old ctty now to prevent that.
     567         * Use O_NONBLOCK: old ctty may be a serial line.
     568         */
     569        fd = open("/dev/tty", O_RDWR | O_NONBLOCK);
     570        if (fd >= 0) {
     571            /* TIOCNOTTY sends SIGHUP to the foreground
     572             * process group - which may include us!
     573             * Make sure to not die on it:
     574             */
     575            sighandler_t old = signal(SIGHUP, SIG_IGN);
     576            ioctl(fd, TIOCNOTTY);
     577            close(fd);
     578            signal(SIGHUP, old);
     579        }
     580    }
     581
     582    /* Close stdio, and stray descriptors, just in case */
    601583    n = xopen(bb_dev_null, O_RDWR);
    602584    /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
     
    622604
    623605    /* Open the tty as standard input, if it is not "-" */
    624     /* If it's not "-" and not taken yet, it will become our ctty */
    625606    debug("calling open_tty\n");
    626     open_tty(options.tty);
    627     ndelay_off(0);
     607    open_tty();
     608    ndelay_off(STDIN_FILENO);
    628609    debug("duping\n");
    629     xdup2(0, 1);
    630     xdup2(0, 2);
     610    xdup2(STDIN_FILENO, 1);
     611    xdup2(STDIN_FILENO, 2);
     612
     613    /* Steal ctty if we don't have it yet */
     614    tsid = tcgetsid(STDIN_FILENO);
     615    if (tsid < 0 || pid != tsid) {
     616        if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0)
     617            bb_perror_msg_and_die("TIOCSCTTY");
     618    }
     619
     620#ifdef __linux__
     621    /* Make ourself a foreground process group within our session */
     622    if (tcsetpgrp(STDIN_FILENO, pid) < 0)
     623        bb_perror_msg_and_die("tcsetpgrp");
     624#endif
    631625
    632626    /*
     
    638632     * 5 seconds seems to be a good value.
    639633     */
    640     if (tcgetattr(0, &termios) < 0)
     634    if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0)
    641635        bb_perror_msg_and_die("tcgetattr");
    642636
    643     pid = getpid();
    644 #ifdef __linux__
    645 // FIXME: do we need this? Otherwise "-" case seems to be broken...
    646     // /* Forcibly make fd 0 our controlling tty, even if another session
    647     //  * has it as a ctty. (Another session loses ctty). */
    648     // ioctl(0, TIOCSCTTY, (void*)1);
    649     /* Make ourself a foreground process group within our session */
    650     tcsetpgrp(0, pid);
    651 #endif
    652 
    653637    /* Update the utmp file. This tty is ours now! */
    654     update_utmp(pid, LOGIN_PROCESS, options.tty, "LOGIN", fakehost);
    655 
    656     /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
    657     debug("calling termios_init\n");
    658     termios_init(&termios, options.speeds[0], &options);
     638    update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost);
     639
     640    /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */
     641    debug("calling init_tty_attrs\n");
     642    init_tty_attrs(G.speeds[0]);
    659643
    660644    /* Write the modem init string and DON'T flush the buffers */
    661     if (options.flags & F_INITSTRING) {
     645    if (option_mask32 & F_INITSTRING) {
    662646        debug("writing init string\n");
    663         full_write1_str(options.initstring);
     647        full_write1_str(G.initstring);
    664648    }
    665649
    666650    /* Optionally detect the baud rate from the modem status message */
    667651    debug("before autobaud\n");
    668     if (options.flags & F_PARSE)
    669         auto_baud(line_buf, sizeof(line_buf), &termios);
     652    if (option_mask32 & F_PARSE)
     653        auto_baud();
    670654
    671655    /* Set the optional timer */
    672     alarm(options.timeout); /* if 0, alarm is not set */
     656    signal(SIGALRM, alarm_handler);
     657    alarm(G.timeout); /* if 0, alarm is not set */
    673658
    674659    /* Optionally wait for CR or LF before writing /etc/issue */
    675     if (options.flags & F_WAITCRLF) {
     660    if (option_mask32 & F_WAITCRLF) {
    676661        char ch;
    677 
    678662        debug("waiting for cr-lf\n");
    679663        while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
    680664            debug("read %x\n", (unsigned char)ch);
    681             ch &= 0x7f;                     /* strip "parity bit" */
    682665            if (ch == '\n' || ch == '\r')
    683666                break;
     
    686669
    687670    logname = NULL;
    688     if (!(options.flags & F_NOPROMPT)) {
    689         /* NB:termios_init already set line speed
    690          * to options.speeds[0] */
     671    if (!(option_mask32 & F_NOPROMPT)) {
     672        /* NB: init_tty_attrs already set line speed
     673         * to G.speeds[0] */
    691674        int baud_index = 0;
    692675
    693676        while (1) {
    694             /* Read the login name. */
     677            /* Read the login name */
    695678            debug("reading login name\n");
    696             logname = get_logname(line_buf, sizeof(line_buf),
    697                     &options, &chardata);
     679            logname = get_logname();
    698680            if (logname)
    699681                break;
    700             /* we are here only if options.numspeed > 1 */
    701             baud_index = (baud_index + 1) % options.numspeed;
    702             cfsetispeed(&termios, options.speeds[baud_index]);
    703             cfsetospeed(&termios, options.speeds[baud_index]);
    704             tcsetattr_stdin_TCSANOW(&termios);
     682            /* We are here only if G.numspeed > 1 */
     683            baud_index = (baud_index + 1) % G.numspeed;
     684            cfsetspeed(&G.tty_attrs, G.speeds[baud_index]);
     685            set_tty_attrs();
    705686        }
    706687    }
    707688
    708     /* Disable timer. */
     689    /* Disable timer */
    709690    alarm(0);
    710691
    711     /* Finalize the termios settings. */
    712     termios_final(&options, &termios, &chardata);
    713 
    714     /* Now the newline character should be properly written. */
    715     full_write(STDOUT_FILENO, "\n", 1);
    716 
    717     /* Let the login program take care of password validation. */
     692    finalize_tty_attrs();
     693
     694    /* Let the login program take care of password validation */
    718695    /* We use PATH because we trust that root doesn't set "bad" PATH,
    719      * and getty is not suid-root applet. */
     696     * and getty is not suid-root applet */
    720697    /* With -n, logname == NULL, and login will ask for username instead */
    721     BB_EXECLP(options.login, options.login, "--", logname, NULL);
    722     bb_error_msg_and_die("can't execute '%s'", options.login);
    723 }
     698    BB_EXECLP(G.login, G.login, "--", logname, NULL);
     699    bb_error_msg_and_die("can't execute '%s'", G.login);
     700}
Note: See TracChangeset for help on using the changeset viewer.