Thursday, April 19, 2012

Blocking echo on terminal

There may be situation where in you may have to stop echo of characters on console while user types in. For ex: when prompted for password. This can be done very easily by using set of glibc library calls namely tcgetattr and tcsetattr. These library calls internally use ioctl on hardware to achieve the required functionality on terminal. You can achieve with following flow.

1)      Get old terminal configuration using tcgetattr
2)      Mask off echo
3)      Set new configuration to the terminal immediately using tcsetattr and TCSANOW.
4)      Do the work.
5)      Restore the old terminal settings.

Simple is it. So here is the code tested on linux box. Hope you will find it useful. Again I have tried to maintain at-most modularity so that these things can be used as APIs. Code should be portable easily across OSes using glibc.

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <stdint.h>

#define TRUE  1
#define FALSE 0

int term_get_current (struct termios* term)
{
    if (term && !tcgetattr(STDIN_FILENO, term)) {
        return TRUE;
    }

    return FALSE;
}

int term_echo_set (struct termios* term, uint8_t echo_flag)
{
    if (term) {
        term->c_lflag = echo_flag? (term->c_lflag | ECHO):(term->c_lflag & ~ECHO);
        if (!tcsetattr(STDIN_FILENO, TCSANOW, term)) {
            return TRUE;
        }
    }

    return FALSE;
}

int term_restore (struct termios* term)
{
    if (term) {
        if(!tcsetattr(STDIN_FILENO, TCSANOW, term)) {
            return TRUE;
        }
    }

    return FALSE;
}

int main()
{
    struct termios term_old, term_new;
    char buf[50] = {0};

    if (!term_get_current(&term_old)) {
        printf("Unable to get terminal current instance\n");
        goto end;
    }

    term_new = term_old;

    printf("Password Please: ");

    if (!term_echo_set(&term_new, FALSE)) {
        printf("Unable to turn terminal echo off\n");
        goto end;
    }

    scanf("%s", buf);

    /*
     * If turning back echo ON does not succeed, do not exit!
     * Instead proceed to restore old terminal settings
     * In case of failure, do not try to echo the password
     */
    if (!term_echo_set(&term_new, TRUE)) {
        printf("Unable to turn terminal echo on\n");       
    } else {
        printf("\n\nYour password is: %s\n", buf);
    }

    if (!term_restore(&term_old)) {
        printf("Unable to restore old terminal settings\n");
    }

end: return 0;   
}


Compile using gcc. I have not shown output here since it needs to be tried on your own machines.

Off-Topic:

The man pages of tcsetattr has lot of other interesting options to play with terminal. Just have look at man pages.

Please leave comments or suggestions if you have.