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

init.c

/*
 * fbgetty : a getty for framebuffer 
 * 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.  
 *
 */

/*
 * Here is code that test and open devices, update utmp and 
 * signal handling
 *
 */

#include <fbgetty/global.h>

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

#include <stdarg.h>

#include <locale.h>
#include <string.h> 

#include <fcntl.h>

#include <utmp.h>

#include <errno.h>

#include <syslog.h>
#include <signal.h>

#include <termios.h>
#include <sys/ioctl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>

#include <termios.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

/* Find the major and minor macros (probably defined in sys/type.h */
#if !defined(major) && !defined(minor)
# ifdef MAJOR_IN_SYSMACROS
#  include <sys/sysmacros.h>
# endif
# ifdef MAJOR_IN_MKDEV
#  include <sys/mkdev.h>
# endif
# if defined(__linux__) && !defined(MAJOR_IN_MKDEV) && !defined(MAJOR_IN_SYSMACROS)
#  include <linux/kdev_t.h>
#  define major(rdev) MAJOR(rdev)
#  define minor(rdev) MINOR(rdev)
# endif
#endif

#ifdef __linux__
# include <linux/major.h>
# include <linux/tty.h>
# include <linux/vt.h>
# ifdef USE_FRAME_BUFFER
#  include <linux/fb.h>
/* Extracted (and modified) from linux/Documentation/devices.txt
 29 char        Universal frame buffer
                  0 = /dev/fb0          First frame buffer
                  1 = /dev/fb1          Second frame buffer
                  2 = /dev/fb2
                  3 = /dev/fb3
                    ...
                 31 = /dev/fb31         32nd frame buffer

                For backwards compatibility {2.6} the following
                progression is also handled by current kernels:
                  0 = /dev/fb0
                 32 = /dev/fb1
                 64 = /dev/fb2
                 96 = /dev/fb3
                    ...
                224 = /dev/fb7
*/
/* a little hack to support post 2.3.38 framebuffer devices */
#  include <linux/version.h> 
#  if (LINUX_VERSION_CODE) >= (KERNEL_VERSION(2,3,38))
#   ifndef FB_MODES_SHIFT
#    define FB_MODES_SHIFT 5
#   endif
#  endif
# endif 
#endif

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


#ifdef USE_VT_SWITCHING
#include <fbgetty/vt.h>
#endif

#include <fbgetty/kbd.h>

/* setup device owner and mode, for devfs or old device scheme
   first check for devfs name,
   if fail use old names 

  access to vcsa3:
  setup_device(3, "vcc/a", "vcsa", NULL);

  access to hda1:
  setup_device(1, "ide/host0/target0/lun0/part", "ide/hd/c0b0t0u0p",  "hda", NULL);
 
*/
static int
setup_devices(int device, char *path_dev, ...)
{
  va_list args;
  struct stat st;
  char path[4096];

  va_start(args, path_dev);

  while(path_dev != NULL)
    {
      snprintf(path, 4096, "%s/%s%d", _PATH_DEV, path_dev, device);
      if (stat (path, &st) != -1)
      break;

      path_dev = va_arg(args, char *); /* read the next path */
    }

  if (path_dev != NULL)
    {
      chown(path, 0, st.st_gid); /* keep group */
      chmod(path, 0600);
    }

  va_end(args);
  
  return (path_dev == NULL) ? -1 : 0;
}


#ifndef HAVE_CTERMID
static const char *ctty = "/dev/tty";
#define get_ctty() ctty
#else
#define get_ctty() ctermid(NULL) /* under GNU system, ctermid() always return "/dev/tty" */
#endif

/* try to open /dev/tty, if an error occur, there's no controlling tty */
int
check_ctty(void)
{
  int fd;

  fd = open(get_ctty(), O_RDWR); /* if we can open this,
                            fbgetty has a controlling terminal */
  if (fd != -1)
    close(fd);

  return fd;
}

int
close_ctty(void)
{
  /* Close old std devices */
  close (STDERR_FILENO);
  close (STDOUT_FILENO);
  close (STDIN_FILENO);

  if (fgoptions->tty_fd != -1)
    close(fgoptions->tty_fd);

  return 0;
}

int
detach_ctty(void)
{
  int fd;

  /* detach from controlling terminal, setsid() must have already done that */
  fd = open(get_ctty(), O_RDWR); /* if we can open this,
                        fbgetty has a controlling terminal */
  if (fd != -1)
     {
#ifdef FB_GETTY_DEBUG
       error("cant' open ctty '%s': %s", ctty, strerror(errno));
#endif

#ifdef TIOCNOTTY
       if (ioctl(fd, TIOCNOTTY, 0) == -1) /* detach from it */
       error("ioctl(TIOCNOTTY) failed: can't detach from controlling terminal %s", strerror(errno));
#endif
       close(fd);
     }

  /* in all other case, close current tty */
  close_ctty();

  return 0;
}

/*
 * open a controlling tty
 *
 */
int 
open_ctty(void)
{
  int fd;
  int i;

  signal(SIGHUP, SIG_IGN);

  /* open the tty device (stdout) */
  fgoptions->tty_fd = open(fgoptions->tty_device, O_RDWR);
  if (fgoptions->tty_fd == -1) 
    fatal_error("can't open tty device %s: %s", fgoptions->tty_device, strerror(errno));

#ifdef TIOCSCTTY
  /* steal the controlling tty */
  if (ioctl (fgoptions->tty_fd, TIOCSCTTY, (void *) 1) == -1)
    error("ioctl TIOCSCTTY failed: %s", strerror(errno));
#endif

  /* reopen std devices */
  for(i = 0; i < 3; i ++)
    if (fgoptions->tty_fd != i)  /* prevent already opened fd */
      {
#ifdef HAVE_DUP2
      fd = dup2(fgoptions->tty_fd, i);  
      if (fd == -1)
        fatal_error("dup2() failed: %s", strerror(errno));
#else /* ! HAVE_DUP2 */
      close(i);
# ifdef HAVE_DUP
      fd = dup(fgoptions->tty_fd); 
      if (fd == -1)
        fatal_error("dup() failed: %s", strerror(errno));
# else /* !HAVE_DUP */
      fd = open(fgoptions->tty_device, O_RDWR);
      if (fd == -1)
        fatal_error("open(%s) failed: %s", fgoptions->tty_device, strerror(errno));
# endif /* !HAVE_DUP */
      if (fd != i)
        error("can't dup() file descriptor %d to %d", fgoptions->tty_fd, i);
#endif /* !HAVE_DUP2 */
      }

  signal(SIGHUP, SIG_DFL);

  return 0;
}

int
save_ctty(void)
{
  int fd;


  /* keep the controlling terminal openned elsewhere 
     than STD_* file descriptor */

#ifdef HAVE_DUP2

  if (fcntl(255, F_GETFD) != -1) /* act like bash */
    close(255);

  fd = dup2(fgoptions->tty_fd, 255);

#else /* !HAVE_DUP2 */
/* try another method */
# warning "----------------------"
# warning "You shouldn't use that"
# warning "----------------------"

  int i;

  do
    {
# ifdef HAVE_DUP
      fd = dup(fgoptions->tty_fd);
# else
      fd = open(fgoptions->tty_device, O_RDWR);
# endif
    }
  while(fd != -1 && fd < 3);

#endif /* !HAVE_DUP2 */
  if (fd == -1)
    return -1;
  
  if (fgoptions->tty_fd > 2)
    close(fgoptions->tty_fd); /* close the old, if it's not a std fd */

  fgoptions->tty_fd = fd;
  
  if (fgoptions->tty_fd > 2)
    fcntl(fgoptions->tty_fd, F_SETFD, FD_CLOEXEC);

  return 0;
}


/* 
 * initialize fbgetty 
 * parameters are set by parse_command_line()
 * 
 */
int 
init (void)
{
  struct termios term;
  struct stat st;

  pid_t pid,sid,pgid, pgrp;

#ifdef USE_FRAME_BUFFER
  struct fb_con2fbmap con2fbmap;
#endif

  /* 
   * set locale used 
   * This doesn't work for date on my system
   * but error message from the libc are translated
   *
   */
#ifdef FB_GETTY_DEBUG
  error("old locale: %s", setlocale(LC_ALL,NULL));
#endif

  setlocale(LC_ALL,"");

#ifdef FB_GETTY_DEBUG
  error("new locale: %s", setlocale(LC_ALL,NULL));
#endif

  /* install the default signal handlers */
  install_signal();

  /* 
   * I think it's not fatal if fbgetty is not running as root 
   * but bad things could happen 
   */
  if ((getuid() != 0) || (getgid() != 0))
    {
      error("WARNING: %s should be run as root on %s",
          program_invocation_short_name,
          fgoptions->tty_device);
      error("WARNING: i will try to continue");
    }

#ifdef FB_GETTY_DEBUG
  error("testing devices");
#endif

  /* 
   * test of tty device 
   * if not valid, leave the program 
   */
  if (fgoptions->tty_device != NULL)
    {
      if (stat (fgoptions->tty_device, &st) == -1) 
      fatal_error("stat %s: %s",fgoptions->tty_device, strerror(errno));

      /* test if it's a character device */
      if (!(S_ISCHR(st.st_mode)))
      fatal_error("%s is not a character device",fgoptions->tty_device);

#if defined(TTY_MAJOR) && defined(MAX_NR_CONSOLE)
      if ((major(st.st_rdev) != TTY_MAJOR) || (minor(st.st_rdev) > MAX_NR_CONSOLES))
      fatal_error("%s: invalid tty device",fgoptions->tty_device);
#endif

      fgoptions->tty_number = minor(st.st_rdev);

      /* own the tty */
      chown(fgoptions->tty_device, 0, st.st_gid);
      chmod(fgoptions->tty_device, 0600); /* or 0620 to allow message to be sent to the tty */

#ifdef __linux__
      /* setup vcs */
      setup_devices(fgoptions->tty_number, "vcc/", "vcs", NULL);
      /* setup vcsa */
      setup_devices(fgoptions->tty_number, "vcc/a", "vcsa", NULL);
#endif /* __linux__ */
    }

#ifdef USE_FRAME_BUFFER
  /* test of frame buffer device */
  /* if not valid ignore it */
  if (fgoptions->fb_device != NULL)
    {
      if ((stat(fgoptions->fb_device, &st) == -1) 
        || !(S_ISCHR(st.st_mode)) 
        || (major(st.st_rdev) != FB_MAJOR))
          {
            error("invalid framebuffer device '%s'", fgoptions->fb_device);
            error("framebuffer disabled");

            free(fgoptions->fb_device);
            fgoptions->fb_device = NULL;
          }
      else
      {
        /* found the framebuffer number */
        fgoptions->fb_number = -1;

        /* XXX find a way to be able to use the other method in case of trouble */
#define USE_FB_DEVICE_MINOR 1

#ifdef USE_FB_DEVICE_MINOR
        /* in linux < 2.3.38, FB_MAX = 8 
           in linux > 2.3.38, FB_MAX = 32 */
        if (minor(st.st_rdev) >= FB_MAX)
          {
            /* first try to convert an old device minor number */
            fgoptions->fb_number = minor(st.st_rdev) >> FB_MODES_SHIFT;
          }

        /* probably a post 2.3.38 device node, use directly the minor number */
        if (fgoptions->fb_number <= 0)
          {
            /* framebuffer number is the minor number */
            fgoptions->fb_number = minor(st.st_rdev);
          }

        /* WARNING: the con2fbmap ioctl could fail in case of a an old kernel
         * that want minor in 0 32 64 96 ... 224
         * because we get only the fb number 0 1 2 3 4 6 ....
         */

#else /* !USE_FB_DEVICE_MINOR */

        char *ptr;

        /* if all fails, try to get it by it's filename */
        /* get the start of the number */
        for(i = strlen(fgoptions->fb_device), i--; i > 0 && !isdigit(fgoptions->fb_device[i]) ; i--);

        /* try to convert */
        fgoptions->fb_number = strtoul(fgoptions->fb_device + i, &ptr , 10);

        /* something failed */
        if (*ptr != '\0')
          {
            /* cannot get the framebuffer device number */
            error("cannot guess the framebuffer number for device '%s'", fgoptions->fb_device);
            error("framebuffer disabled");

            free(fgoptions->fb_device);
            fgoptions->fb_device = NULL;
          }

#endif /* !USE_FB_DEVICE_MINOR */

      }

    }
#endif
 
  /* 
   * session, pid, gid, process group setup, etc ... 
   */

  pid = getpid();
  sid = getsid(pid);
  pgid = getpgid(pid);

#ifdef FB_GETTY_DEBUG
  error("pid = %d", pid);
  error("sid = %d", sid);
  error("pgid = %d", pgid);
#endif

  if (pid != sid) /* someone try to run fbgetty outside of init, fbgetty does have is own session */
    {
      if (pgid == pid) /* no session, but have process group */
        {
        /* 
         * can't setsid successfully if fbgetty is already leader of a
         * process group (pid == pgid), so need to fork for a
         * new pid (which will end up in the current process group)
         */

          pid_t child;

        child = fork();
        switch(child)
          {
          case -1:
            fatal_error("fork() failed: %s", strerror(errno));
            break;

          default: /* father code */
#if 0
            /* XXX must add an option to handle this case:
             must the father wait child return ?
             in this case, father must clean utmp
             in the other case, should the child fork again to do like the first case ? */ 
#else
            fgcleanup();
            free(fgoptions_free(fgoptions));
            _exit(EXIT_SUCCESS);
#endif
            break;

          case 0: /* in child */
            pid = getpid(); /* update value */
          }
        }

      /* then get the session */
      sid = setsid();
      if (sid == -1)
      fatal_error("setsid() failed: Can't get our own session: %s", strerror(errno));

      /* update value */
      pgid = getpgid(pid); /* if everything is ok setsid() give fbgetty a process group */
    }

#ifdef FB_GETTY_DEBUG
  error("pid = %d", pid);
  error("sid = %d", sid);
  error("pgid = %d", pgid);
#endif  

  /*
   * be sure fbgetty have is own process group
   */
  if (pgid != pid)
    {
#ifdef FB_GETTY_DEBUG
      error("setting pgid");
#endif
      /* get our own process group*/
      if (setpgid(pid, pid) == -1)
      error("setpgid(%d, %d) failed: %s", pid, pid, strerror(errno));

      pgid = getpgid(pid); /* update the value */
    }

#ifdef FB_GETTY_DEBUG
  error("pid = %d", pid);
  error("sid = %d", sid);
  error("pgid = %d", pgid);
#endif  

  detach_ctty(); /* detach from a possible already connected controling terminal */
  open_ctty(); /* open our controlling terminal */

  /* vhangup() on the current line, must be done after the new tty is openned */
  /* vhangup() will replace all open file descriptors that point to our
     controlling tty by a dummy that will deny further reading/writing
     to our device (for all processes using the device).
     It will also reset the tty to sane defaults, so we
     don't have to modify the tty device for sane settings.
     We also get a SIGHUP/SIGCONT (taken from mingetty) */
  do_vhangup(); /* linux specific */

  close_ctty(); /* no need to make an hard detach, just close it ?  */
  open_ctty(); /* reopen our new clean controlling tty */

  save_ctty(); /* keep an fd openned on the controlling terminal */

  if (check_ctty() == -1)
    error("no controlling tty: %s", strerror(errno));

#ifdef FB_GETTY_DEBUG
  error("after openning tty");

  pid = getpid();
  sid = getsid(pid);
  pgid = getpgid(pid);

  error("pid = %d", pid);
  error("sid = %d", sid);
  error("pgid = %d", pgid);
#endif

  /* get the process group associated to the terminal */
  pgrp = tcgetpgrp(fgoptions->tty_fd);
  if (pgrp == -1)
    {
      error("tcgetpgrp() : %s", strerror(errno));
    }
  else
    {
#ifdef FB_GETTY_DEBUG
      error("pgrp = %d", pgrp);
#endif
      if (pgrp != pgid)
      {
        error("terminal control must be gainned");
        /* this make gcc bugging if this function is called, so test tcgetpgrp() before */
        if (tcsetpgrp (fgoptions->tty_fd, pgid) == -1)
          error("can't run tcsetpgrp(): %s", strerror(errno));
      }
    }

  fgoptions->master = TRUE; /* at this time, fbgetty control the terminal */
  
  /*
   * Don't buffer... the output,input 
   */
  setvbuf (stdout, NULL,_IONBF,(size_t) 0);
  setvbuf (stdin , NULL,_IONBF,(size_t) 0);
  setvbuf (stderr, NULL,_IONBF,(size_t) 0);

  ioctl (STDIN_FILENO, TCFLSH, 0);   /* flush pending input */

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

  /* 8bit, local */
  term.c_cflag &= ~(CSIZE);
  term.c_cflag |= CS8 | CLOCAL | HUPCL | CREAD;

  term.c_iflag &= ~(ISTRIP) ;
  term.c_iflag |= IXON | IXOFF;                /* 2-way flow control */

  term.c_lflag &= ~(IEXTEN) ;
  term.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE;  /* no longer| ECHOCTL | ECHOPRT*/

  term.c_oflag |= OPOST;

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

  init_kbd();  /* init kbd state */

#ifdef USE_FRAME_BUFFER
  if (fgoptions->fb_device != NULL)
    {
      /* Opening the framebuffer */
      fgoptions->fb_fd = open(fgoptions->fb_device , O_RDWR);
      if (fgoptions->fb_fd == -1)
      {
        error("can't open framebuffer device %s : %s",fgoptions->fb_device, strerror(errno));
        free(fgoptions->fb_device);
        fgoptions->fb_device = NULL;
      }
      else
      {
        /* map the console to other framebuffer (if not fb0) missing in previous release (0.1.2) */
        con2fbmap.console = fgoptions->tty_number;
        con2fbmap.framebuffer = fgoptions->fb_number;

        if (ioctl(fgoptions->fb_fd, FBIOPUT_CON2FBMAP, &con2fbmap) == -1 ) 
          {
            /* can't map framebuffer,
             disabling it. This behavior could be changed in the future */
            error("mapping framebuffer to VT failed: %s", strerror(errno));
            close(fgoptions->fb_fd);
            free(fgoptions->fb_device);
            fgoptions->fb_device = NULL;
          }
      }
    }
#endif  /* USE_FRAME_BUFFER */

#ifdef FB_GETTY_DEBUG
  error("console: %d",fgoptions->tty_number);
  error("tty fd: %d",fgoptions->tty_fd);
#ifdef USE_FRAME_BUFFER
  error("framebuffer: %d",fgoptions->fb_number);
  error("fb fd: %d",fgoptions->fb_fd);
#endif
#endif /* FB_GETTY_DEBUG */

#ifdef USE_VT_SWITCHING
  /* install the refresh code */
  vt_handler_init();
#endif /* USE_VT_SWITCHING */

  return(0);
}

Generated by  Doxygen 1.6.0   Back to index