/* 
   Copyright (C) 1994 Free Software Foundation

   This program 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.

   This program 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., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include <hurd.h>
#include "dev.h"

#include <stdio.h>
#include <string.h>
#include <grp.h>
#include <fcntl.h>

/* Read the config file from CONF.  For each device in the DEVICES array
   which is not listed in CONF, initialize it according to the usual
   rules.  Ignore devices listed in CONF which are not in the DEVICES
   array. */
void
read_config_file (FILE *conf)
{
  char *linebuf = 0, *line;
  size_t linebufsize = 0;
  char *p;
  char *fields[13];
  int nfield;
  int lineno;
  int i;
  int n;
  int abort_line;
  struct device *dev;
  int val;
  struct group *opergroup = 0;
  int triedgetgrp = 0;
  struct timeval time_now = {0, 0};

  configdirty = 0;

  lineno = 0;
  for (;;)
    {
      n = getline (&linebuf, &linebufsize, conf);
      if (n == -1)
	{
	  if (feof (conf))
	    break;
	  perror ("Reading config file");
	  exit (1);
	}

      lineno++;

      /* comment lines */
      if (n && *linebuf == '#')
	continue;
      
      /* Each line looks like this:

mach-dev-name raw mode uid gid author flags 
atime atime_usec mtime mtime_usec ctime ctime_usec 
      
      Except, of course, it isn't split.  The `raw' field is either
      `raw' or `noraw' for disks, tapes, and ttys, and `null' for
      other devices.  The other fields are all numeric.  The
      declaration of FIELDS above knows that there are thirteen
      elements here. */

      line = linebuf;
      nfield = 0;
      abort_line = 0;
      while (p = strsep (&line, " \t"))
	{
	  if (nfield == 13)
	    {
	      fprintf (stderr, "Too many fields on line %d\n", lineno);
	      abort_line = 1;
	      break;
	    }
	  fields[nfield++] = p;
	}
      if (abort_line)
	continue;
      if (nfield != 13)
	{
	  fprintf (stderr, "Not enough fields on line %d\n", lineno);
	  continue;
	}

      /* Look for this device in the device list */
      for (i = 0; i < ndevices; i++)
	if (!strcmp (devices[i].name, fields[0])
	    && (!!devices[i].raw == !strcmp (fields[1], "raw")))
	  break;

      /* We didn't find it; on to the next line. */
      if (i == ndevices)
	continue;

      dev = &devices[i];

      /* Initialize bits of state that we don't get from the config file. */
      dev->st.st_fstype = FSTYPE_DEV;
      dev->st.st_fsid = 0;	/* ??? */
      dev->st.st_ino = i;	/* ??? */
      dev->st.st_nlink = 1;
      dev->st.st_size = 0;
      dev->st.st_blocks = 0;

      /* field 2 is mode */
      val = strtol (fields[2], 0, 0);

      /* Non-raw tapes and disks are S_IFBLK; all else are S_IFCHR;
	 FD directory is a dir, of course. */
      if (!dev->raw &&
	  (dev->type == DEV_TYPE_TAPE || dev->type == DEV_TYPE_DISK))
	dev->st.st_mode = (val & ~(S_IFMT|S_ISPARE)) | S_IFBLK;
      else if (dev->type == DEV_TYPE_FDDIR)
	dev->st.st_mode = (val & ~(S_IFMT|S_ISPARE)) | S_IFDIR;
      else
	dev->st.st_mode = (val & ~(S_IFMT|S_ISPARE)) | S_IFCHR;

      if (dev->st.st_mode != val)
	configdirty = 1;
      
      /* field 3 is uid */
      dev->st.st_uid = strtol (fields[3], 0, 0);
      
      /* field 4 is gid */
      dev->st.st_gid = strtol (fields[4], 0, 0);
      
      /* field 5 is author */
      dev->st.st_author = strtol (fields[5], 0, 0);
      
      /* field 6 is flags */
      dev->st.st_flags = strtol (fields[6], 0, 0);
      
      /* field 7 is atime seconds */
      dev->st.st_atime = strtol (fields[7], 0, 0);
      
      /* field 8 is atime microseconds */
      dev->st.st_atime_usec = strtol (fields[8], 0, 0);
      
      /* field 9 is mtime seconds */
      dev->st.st_mtime = strtol (fields[9], 0, 0);
      
      /* field 10 is mtime microseconds */
      dev->st.st_mtime_usec = strtol (fields[10], 0, 0);
      
      /* field 11 is ctime seconds */
      dev->st.st_ctime = strtol (fields[11], 0, 0);

      /* field 12 is ctime microseconds */
      dev->st.st_ctime_usec = strtol (fields[12], 0, 0);
    }

  /* At this point we have read in the config file.  Now look for
     devices that we didn't find in the config file and create 
     stat structures for them */
  for (i = 0; i < ndevices; i++)
    {
      dev = &devices[i];
      if (dev->st.st_mode)
	continue;
      
      configdirty = 1;

      dev->st.st_fstype = FSTYPE_DEV;
      dev->st.st_fsid = 0;	/* ??? */
      dev->st.st_ino = i; /* ??? */
      dev->st.st_nlink = 1;
      dev->st.st_size = 0;
      dev->st.st_blocks = 0;

      /* Conventionally:
	 Disks are mode 640.
	 Tapes are mode 666.
	 Hires devices are mode 666.
	 Magic devices are mode 666.
	 We set everything else to mode 600 to be safe.  */
      switch (dev->type)
	{
	case DEV_TYPE_DISK:
	  if (dev->raw)
	    dev->st.st_mode = S_IFCHR | 0640;
	  else
	    dev->st.st_mode = S_IFBLK | 0640;
	  break;
	  
	case DEV_TYPE_TAPE:
	  if (dev->raw)
	    dev->st.st_mode = S_IFCHR | 0666;
	  else
	    dev->st.st_mode = S_IFBLK | 0666;
	  break;
	  
	case DEV_TYPE_HIRES:
	case DEV_TYPE_NULL:
	case DEV_TYPE_CTTY:
	case DEV_TYPE_FD:
	  dev->st.st_mode = S_IFCHR | 0666;
	  break;
	  
	case DEV_TYPE_FDDIR:
	  dev->st.st_mode = S_IFDIR | 0555;
	  break;

	default:
	  dev->st.st_mode = S_IFCHR | 0600;
	}
      
      /* Devices all start out owned and authored by root */
      dev->st.st_uid = dev->st.st_author = 0;
      
      /* Disks are group `operator', everything else is group 0. */
      if (dev->type == DEV_TYPE_DISK)
	{
	  if (!opergroup && !triedgetgrp)
	    {
	      opergroup = getgrnam ("operator");
	      if (!opergroup)
		fprintf (stderr, "Group `operator' doesn't exist!\n");
	      triedgetgrp = 1;
	    }
	  if (opergroup)
	    dev->st.st_gid = opergroup->gr_gid;
	  else
	    /* Well, that group doesn't exist.  We tried. */
	    dev->st.st_gid = 0;
	}
      else
	dev->st.st_gid = 0;
      
      dev->st.st_flags = 0;

      /* Initialize TIME_NOW if necessary */
      if (time_now.tv_sec == 0)
	gettimeofday (&time_now, 0);

      dev->st.st_atime = dev->st.st_mtime = dev->st.st_ctime = time_now.tv_sec;
      dev->st.st_atime_usec = dev->st.st_mtime_usec = dev->st.st_ctime_usec
	= time_now.tv_usec;
    }
}

struct mutex config_lock = MUTEX_INITIALIZER;

/* If CONFIGDIRTY is set, then write out a new copy of the
   configuration file corresponding to the current contents
   of DEVICES.  */
void
sync_config_file ()
{
  int i;
  FILE *conf;
  struct device *dev;

  mutex_lock (&config_lock);
  
  if (!configdirty)
    return;
  
  mach_port_mod_refs (mach_task_self (), config_file,
		      MACH_PORT_RIGHT_SEND, 1);
  file_truncate (config_file, 0);
  conf = fdopen (openport (config_file, O_WRITE), "w");

  for (i = 0; i < ndevices; i++)
    {
      dev = &devices[i];
      
      fprintf (conf, "%s %s %#o %d %d %d %#x %ld %ld %ld %ld %ld %ld\n",
	       dev->name,
	       (dev->raw ? "raw" :
		((dev->type == DEV_TYPE_DISK 
		  || dev->type == DEV_TYPE_TAPE
		  || dev->type == DEV_TYPE_TTY) ? "noraw" : "null")),
	       dev->st.st_mode,
	       dev->st.st_uid, dev->st.st_gid, dev->st.st_author,
	       dev->st.st_flags,
	       dev->st.st_atime, dev->st.st_atime_usec,
	       dev->st.st_mtime, dev->st.st_mtime_usec,
	       dev->st.st_ctime, dev->st.st_ctime_usec);
    }
  fclose (conf);
  mutex_unlock (&config_lock);
}

	       
