Logo Search packages:      
Sourcecode: fbgetty version File versions  Download package

prompt.c

/*
 * prompt.c : screen read and write
 *
 * Part of fbgetty 
 * Copyright (C) 1999 2000 2001, Yann Droneaud <ydroneaud@meuh.eu.org>. 
 *
 * fbgetty is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * fbgetty is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 *
 */

#include <fbgetty/global.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <string.h>

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
# include <time.h>
#endif

#include <ctype.h>
#include <errno.h>
#include <utmp.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/poll.h>

#include <termios.h>

#ifdef __linux__
# include <linux/vt.h>
# ifdef USE_FRAME_BUFFER
#  include <linux/fb.h>
# endif
#endif

#include <fbgetty/errors.h>
#include <fbgetty/prompt.h>
#include <fbgetty/options.h>
#include <fbgetty/utmp.h>
#include <fbgetty/issue.h>
#include <fbgetty/sysinfos.h>
#include <fbgetty/vt.h>

#ifdef LOGO_ENABLE
extern int display_logo(unsigned char *fb,
                  struct fb_fix_screeninfo *fix,
                  struct fb_var_screeninfo *var);
#endif

/* this something like a semaphore (but it's not handle as it does !)
 *  (could be protected by signals flags: syscall)
 * function may check it before trying to redraw screen
 */
static volatile int in_refresh = FALSE;

/* indicates the refresh screen is finished */
void
refresh_screen_yield(void)
{
  in_refresh = FALSE;
}

/* return the state of the refresh_screen
  (the value of in_refresh) */
int
refresh_screen_state(void)
{
  return in_refresh;
}

/* indicated the refresh screen is beginning */
void
refresh_screen_acquire(void)
{
  in_refresh = TRUE;
}

/* Try to check
   if we can print things on screen */
int
refresh_screen_check(void)
{
#ifdef USE_VT_SWITCHING
  if (vt_isactive() != TRUE) /* check if vt is active */
    return FALSE;
#endif

#if 0 /* Enable this if problem occur when Scroll Lock is activated */
 {
   struct pollfd p;
   
   /* check if we could write to the terminal */
   p.fd = STDOUT_FILENO;
   p.events = POLLOUT;
   p.revents = 0;
  
   /* don't wait */
   if (poll(&p, 1 , 0) != 0 && 
       (p.revents & POLLOUT) == 0)
     return FALSE;
 }
#endif

  return TRUE;
}

/* Redraw the screen */
void refresh_screen(void)
{

#ifdef USE_FRAME_BUFFER
  struct fb_var_screeninfo fb_var;
  struct fb_fix_screeninfo fb_fix;
  unsigned char *framebuffer;
#endif

  if (refresh_screen_state() != FALSE) /* try prevent two concurrent refresh -this is not atomic */
    {
#ifdef FB_GETTY_DEBUG
      error("- screen is already in refresh");
#endif
      return;
    }    

  refresh_screen_acquire(); /* block other refresh */

  /* add this block of code everywhere ! */
  if (refresh_screen_check() == FALSE)
    {
      refresh_screen_yield();
      return;
    }

#ifdef FB_GETTY_DEBUG
  error("- screen is ok to be refreshed");
#endif

  /* update the system informations */
  get_sysinfos();

  /* check if we can use the screen */
  if (refresh_screen_check() == FALSE)
    {
      refresh_screen_yield();
      return;
    }

#ifdef USE_FRAME_BUFFER
  if (fgoptions->fb_device != NULL)
    {
      if (ioctl(fgoptions->fb_fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
      error("ioctl FBIOGET_FSCREENINFO: %s", strerror(errno));
      
      if (ioctl(fgoptions->fb_fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
      error("ioctl FBIOGET_VSCREENINFO: %s", strerror(errno));
      
#ifdef FB_GETTY_DEBUG
      error("- fb info:");
      error("- X offset vis : %d", fb_var.xoffset);    
      error("- Y offset vis : %d", fb_var.yoffset);
      error("- X virtual : %d", fb_var.xres_virtual);
      error("- Y virtual : %d", fb_var.yres_virtual);
      error("- bits per pixel: %d", fb_var.bits_per_pixel);
#endif
      
      /* map of framebuffer device */
      framebuffer = (unsigned char *) mmap(NULL, 
                                 fb_fix.smem_len, 
                                 PROT_READ | PROT_WRITE,
                                 MAP_SHARED,
                                 fgoptions->fb_fd,
                                 (off_t) NULL);
      if (framebuffer == MAP_FAILED)
      {
        error("cannot mmap frame buffer");
        close (fgoptions->fb_fd);
        free(fgoptions->fb_device);
        fgoptions->fb_device=NULL;
      }
#ifdef FB_GETTY_DEBUG
      else
      {
        error("- framebuffer mapped");
      }
#endif
    }
#endif /* USE_FRAME_BUFFER */

  /* check if we can use the screen */
  if (refresh_screen_check() == FALSE)
    {
#ifdef USE_FRAME_BUFFER
      if (framebuffer != NULL)
      munmap(framebuffer, fb_fix.smem_len);
#endif /* USE_FRAME_BUFFER */

      refresh_screen_yield();
      return;
    }

  /* clear screen (Linux specific) */
  if (fgoptions->screen_clear != FALSE) 
    printf("\033c");  

  print_issue();

  do_prompt();

#ifdef USE_FRAME_BUFFER
  if (fgoptions->fb_device != NULL)
    {
#ifdef LOGO_ENABLE
      display_logo(framebuffer, &fb_fix, &fb_var);
#endif  
      if (framebuffer != NULL)
      munmap(framebuffer, fb_fix.smem_len);
#ifdef FB_GETTY_DEBUG
      error("- framebuffer unmapped\n");
#endif  
    }
#endif /* USE_FRAME_BUFFER */
  
  /* finnish screen refresh */
  refresh_screen_yield();

#ifdef FB_GETTY_DEBUG
  error("- screen is refreshed");
#endif

  return;
}

void
do_prompt (void)
{
  unsigned int i;

#ifdef FB_GETTY_DEBUG
  error("- screen printing prompt");
#endif

  if (fgoptions->login_prompt != NULL && fgoptions->login_prompt[0] != '\0')
    {
      for (i = 0; i < strlen(fgoptions->login_prompt) ; i++)
      {
        if (refresh_screen_check() == FALSE) /* check if we can print on screen */
          return;

        switch(fgoptions->login_prompt[i])
          {
          case '\\':
            print_escape(fgoptions->login_prompt[++i]);
            break;
          case '%':
            print_special(fgoptions->login_prompt[++i]);
            break;
          default:
            putchar (fgoptions->login_prompt[i]);
            break;
          }
      }
    }

  if (refresh_screen_check() != FALSE)
    {
      if (fgoptions->login_name != NULL && *fgoptions->login_name != '\0' )
      printf("%s",fgoptions->login_name);

      fflush (stdout);
    }
}

/* base length of the login name buffer */
#define LOGIN_NAME_ALLOCATED_BASE 32

/* get_login_name */
void
get_login_name (void)
{
  char c;
  int eof; /* if eof == 1, that's EOF
            otherwise is 0 */

  struct termios term, old_term;

  int count;
  int login_allocated;
  int login_length;
  char *new_ptr; 
#ifdef FB_GETTY_DEBUG
  int i;
#endif
  int src;
  int dst;

  if (tcgetattr(STDIN_FILENO, &term) == -1)
    {
      fatal_error("tcgetattr: %s", strerror(errno));
    }

  /* save old term setting for restoring them later */
  memcpy(&old_term, &term, sizeof (struct termios));

  /* we want to read one char at a time, without displaying it */
  term.c_cc[VMIN] = 1;
  term.c_cc[VTIME] = _POSIX_VDISABLE;

  term.c_lflag &= ~( ICANON | ECHO | ISTRIP ) ;

  term.c_cflag &= ~( CSIZE );
  term.c_cflag |= CS8 | CLOCAL ;

  if (tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1)
    {
      fatal_error("tcsetattr: %s", strerror(errno));
    }
 
  fgoptions->login_name_valid = FALSE;

  login_allocated = LOGIN_NAME_ALLOCATED_BASE;

  if (fgoptions->login_name != NULL)
    fgoptions->login_name = (char *) realloc (fgoptions->login_name, login_allocated * sizeof(char));
  else
    fgoptions->login_name = (char *) malloc (login_allocated * sizeof(char));

  if (fgoptions->login_name == NULL)
    {
      fatal_error("login_name alloc() failed: %s", strerror(errno));
    }

  login_length = 0;
  fgoptions->login_name[login_length] = '\0'; /* put the initial NUL char */

#ifdef FB_GETTY_DEBUG
  error("reading login");
#endif

  eof = 0;
  c = '\0';

  do
    {
      /* read a char */
      count = read(STDIN_FILENO, &c, 1);
#ifdef FB_GETTY_DEBUG
      error("* count = %d",count);
#endif
      if (count < 0) 
      {
        if (errno != EINTR)  /* not interrupted by signal ? */
          {
            error("read failed: %s", strerror(errno));
            eof = 1;
          }
        continue; /* the read() call was interrupted */
      }

      if (count == 0 || c == CEOF) /* end of file */
      {
        eof = 1;
        continue;
      }

#ifdef FB_GETTY_DEBUG
      error("* char read: %c (%x)", c, c);
#endif
      /* handle control character */
      switch(c)
      {
      case CERASE:  /* remove a char */
        count = 1;
        goto login_clear;
      case CKILL: /* clear the line */
        count = login_length;
        goto login_clear;
      case CWERASE: /* clear the last word */
        /* first, erase space after the word */
        for(count = login_length - 1; 
            count >= 0 && isspace(fgoptions->login_name[count]); count --);
        /* then erase the word up to the next space */
        for(;count >= 0 && !isspace(fgoptions->login_name[count]); count --);
        count = login_length - count - 1;
      login_clear:
        for(; count > 0 && login_length > 0; count --)
          {
            /* erasing a char from screen */
            putchar('\b'); /* go back */
            putchar(' '); /* clear */

            putchar('\b'); /* rewind */
            
            /* erase the character from the login name */
            login_length --;
            fgoptions->login_name[login_length] = '\0';
          }
        break;

      case CTRL('l'):
      case CREPRINT:
        refresh_screen();
        break;

        /* don't handle those */
      case CQUIT: /* kill fbgetty */
      case CINTR:
      case CSUSP: /* put fbgetty in back ground -> non sense */
        break;

      default:  /* store the character, if it's printable but not a control char */
        if (isprint((int)c) && !iscntrl ((int)c))
          {
            if (login_length >= login_allocated)
            {
              /* get more memory for login name */
              new_ptr = (char *) realloc (fgoptions->login_name,
                                    (login_allocated + LOGIN_NAME_ALLOCATED_BASE) * sizeof(char));
              if (new_ptr == NULL)
                {
                  error("can't allocate more memory for login name");
                  eof = 1;
                  continue;
                }

              /* incr 32 by 32 */
              login_allocated += LOGIN_NAME_ALLOCATED_BASE;

              /* store the char */
              fgoptions->login_name = new_ptr;
            }

            login_length++;
            fgoptions->login_name[login_length] = '\0'; /* put first the new NUL char, 
                                               to be sure the string is terminated */
            fgoptions->login_name[login_length - 1] = c;

            /* print it */
            putchar((int)c);
          }
        break;
      }
    }
  while (eof == 0 /* while not EOF */
       && c != '\r' && c != '\n' /* and no carriage return */);

#define NORMAL_PARANOIA 0
#if defined(NORMAL_PARANOIA) && NORMAL_PARANOIA 
  /* restore old terminal mode */

  /* set mode to echo off, protecting user from typing his password,
     while login wasn't yet operating (idea taken from fgetty-0.5), 
     not enable by default because some login program don't handle it */
  /* fgetty will turn echo off before executing login.  This fixes the
     age-old security problem when the load is heavy at login:
     
     host login: hax0r
     imsol33tPassword: _
  */
  old_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
#endif

  if (tcsetattr(STDIN_FILENO, TCSANOW, &old_term) == -1)
    {
      fatal_error("tcsetattr: %s", strerror(errno));
    }

  /* search for leading space or minus, src will point where the login name start */
  for(src = 0, dst = 0; 
      (fgoptions->login_name[src] != '\0' && (isspace(fgoptions->login_name[src]) ||
                                    fgoptions->login_name[src] == '-')) ;
      src ++);

  /* if there were some space, move the block */
  if (src != dst)
    {
      for(;fgoptions->login_name[src] != '\0'; src ++, dst ++)
      fgoptions->login_name[dst] = fgoptions->login_name[src]; 

      fgoptions->login_name[dst] = '\0';
    }
  else
    {
      dst = strlen(fgoptions->login_name);
    }

  /* remove ending space */
  for(dst --; dst >= 0 && isspace(fgoptions->login_name[dst]); dst --)
    fgoptions->login_name[dst] = '\0';

  fgoptions->login_name_valid = TRUE;

#ifdef FB_GETTY_DEBUG
  error("* login read: %s",fgoptions->login_name);
  for(i = 0; fgoptions->login_name[i] != '\0'; i ++)
    {
      error("* '%c' 0x%02x",fgoptions->login_name[i],fgoptions->login_name[i]);
    }
  error("login read");
#endif

  return;
}

Generated by  Doxygen 1.6.0   Back to index