/* fsboot -- Run programs for mount-point filesystems at boot time.
   Copyright (C) 1994 Free Software Foundation, Inc.
   Written by Roland McGrath.

This file is part of the GNU Hurd.

The GNU Hurd 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.

The GNU Hurd 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 the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* fsboot reads /etc/filesystems for the list of interesting filesystems.
   It then examines the translators of each of these directory names.
   It takes the name of the translator program to be run, appends ".boot",
   and tries to run the resulting file to perform boot-time operations for
   that filesystem.

   All filesystems using the same translator are batched together.
   The .boot program is invoked:
	fs.boot ARGS ... --
		DIR-1 ARGV-1[1] ARGV-1[2] ... ARGV-1[N-1] --
		DIR-2 ARGV-2[1] ARGV-2[2] ... ARGV-2[N-2] --
		...
   ARGS are the command line arguments to fsboot.  DIR-n is the name of the
   directory.  ARGV-n[1..N-n] are the arguments to the translator program
   for directory n.  */

#include <hurd.h>
#include <stdio.h>

int
main (int argc, char **argv)
{
  error_t err;
  FILE *f;
  struct trans
    {
      char *dir;
      char *trans;
      size_t len;
    } *trans;
  int status = 0;
  size_t i, last;

  int trans_compar (void *a, void *b)
    {
      struct trans *ta = a, *tb = b;
      return strncmp (ta->trans, tb->trans,
		      (ta->len < tb->len ? ta :  tb)->len);
    }

  void quit (int sig)
    {
      status = 2;
    }

  f = fopen ("/etc/filesystems", "r");
  if (f == NULL)
    {
      perror ("/etc/filesystems");
      exit (1);
    }
  while (getline (&linebuf, &linebufsize, f) != -1)
    {
      struct trans *nt;
      char *p = index (linebuf, '#');
      if (p)
	*p = '\0';
      p = index (linebuf, '\0');
      while (isspace (p[-1]))
	--p;
      *p = '\0';
      p = linebuf;
      while (isspace (*p))
	++p;

      dir = path_lookup (p, 0, 0);
      if (dir == MACH_PORT_NULL)
	{
	  perror (p);
	  status = 1;
	  continue;
	}

      nt = alloca (sizeof *nt);
      nt->len = 0;
      errno = file_get_translator (dir, &nt->trans, &nt->len);
      mach_port_deallocate (mach_task_self (), dir);
      if (errno)
	{
	  perror ("file_get_translator");
	  status = 1;
	  continue;
	}

      nt->dir = strdup (p);
      if (! nt->dir)
	{
	  perror ("strdup");
	  exit (1);
	}

      nt->next = trans;
      trans = nt;
    }


  /* Sort the translator records by translator program name,
     so records with the same program become adjacent.  */
  qsort (trans, nmp, sizeof (struct trans), trans_compar);

  /* Handle a QUIT signal to exit with nonzero status after processing
     (to get back to a single-user shell from /etc/rc).  */
  signal (SIGQUIT, quit);

  for (last = 0, i = 1; i <= nmp; ++i)
    if (i == nmp || !trans_compar (trans[i], trans[i - 1]))
      {
	/* We have come to a different flavor.
	   Do all the previous ones that were the same flavor.  */
	switch (fork ())
	  {
	  case -1:		/* Fork failed.  */
	    perror ("fork");
	    status = 1;
	    break;

	  default:		/* Parent.  */
	    if (wait (&status) < 0)
	      {
		perror ("wait");
		status = 1;
	      }
	    if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
	      {
		fprintf (stderr, "fsboot: %.*s failed with %d\n",
			 trans[last].len, trans[last].trans, status);
		exit (status);
	      }
	    break;

	  case 0:		/* Child.  */
	    {
	      char *av[argc + nmp * 20];
	      int ac;

	      signal (SIGQUIT, SIG_IGN);

	      /* Construct the name of the program to run from
		 the translator program name and ".boot".  */
	      asprintf (&av[0], "%.*s.boot",
			trans[last].len, trans[last].trans);

	      /* Give the program all our arguments.  */
	      for (ac = 1; ac < argc; ++ac)
		av[ac] = argv[ac];

	      /* Give it arguments telling it where to find
		 send rights for the filesystem nodes to check.  */
	      while (last < i)
		{
		  char *p = trans[last].trans;

		  asprintf (&av[ac++], "%p", mp[i]);

		  while ((p = memchr (p, '\0',
				      trans[last].trans +
				      trans[last].len - p)) != NULL)
		    av[++ac] = ++p;
		  ++i;
		}
	      av[ac] = NULL;
	      
	      execv (av[0], av);
	      perror (av[0]);
	      _exit (127);
	    }
	  }
      }

  return status;
}
