/*
 * lookup_program.c
 *
 * Module for Linux automountd to access a automount map via a query program
 */

#include <stdio.h>
#include <malloc.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define MODULE_LOOKUP
#include "automount.h"

#define MAPFMT_DEFAULT "sun"

#define MODPREFIX "lookup(program): "

#define MAPENT_MAX_LEN 4095

struct lookup_context {
  char *mapname;
  struct parse_mod *parse;
};

int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */

int lookup_init(char *mapfmt, int argc, char **argv, void **context)
{
  struct lookup_context *ctxt;

  if ( !(*context = ctxt = malloc(sizeof(struct lookup_context))) ) {
    syslog(LOG_CRIT, MODPREFIX "malloc: %m");
    return 1;
  }
  if ( argc < 1 ) {
    syslog(LOG_CRIT, MODPREFIX "No map name");
    return 1;
  }
  ctxt->mapname = argv[0];

  if (ctxt->mapname[0] != '/') {
    syslog(LOG_CRIT, MODPREFIX "program map %s is not an absolute pathname",
	   ctxt->mapname);
    return 1;
  }

  if ( access(ctxt->mapname, X_OK) ) {
    syslog(LOG_WARNING, MODPREFIX "program map %s missing or not executable",
	   ctxt->mapname);
  }

  if ( !mapfmt )
    mapfmt = MAPFMT_DEFAULT;

  return !(ctxt->parse = open_parse(mapfmt,MODPREFIX,argc-1,argv+1));
}

int lookup_mount(char *root, char *name, int name_len, void *context)
{
  struct lookup_context *ctxt = (struct lookup_context *) context;
  char mapent[MAPENT_MAX_LEN+1], *p;
  int pipefd[2];
  pid_t f;
  FILE *fp;
  int status;

  syslog(LOG_DEBUG, MODPREFIX "looking up %s", name);

  /* We don't use popen because we don't want to run /bin/sh, and we
     don't use spawnl() because we need the pipe hooks */

  if ( pipe(pipefd) ) {
    syslog(LOG_ERR, MODPREFIX "pipe: %m");
    return 1;
  }
  f = fork();
  if ( f < 0 ) {
    syslog(LOG_ERR, MODPREFIX "fork: %m");
    return 1;
  } else if ( f == 0 ) {
    close(pipefd[0]);
    dup2(pipefd[1],STDOUT_FILENO);
    close(pipefd[1]);
    execl(ctxt->mapname, ctxt->mapname, name, NULL);
    _exit(255);			/* execl() failed */
  }
  close(pipefd[1]);
  fp = fdopen(pipefd[0], "r");
  if ( !fp ) {
    syslog(LOG_ERR, MODPREFIX "fdopen: %m");
    return 1;
  }
  fgets(mapent, MAPENT_MAX_LEN, fp);
  while ( getc(fp) != EOF );
  fclose(fp);
  if ( waitpid(f,&status,0) != f ) {
    syslog(LOG_ERR, MODPREFIX "waitpid: %m");
    return 1;
  }
  if ( !WIFEXITED(status) || WEXITSTATUS(status) != 0 ) {
    syslog(LOG_NOTICE, MODPREFIX "lookup for %s failed", name);
    return 1;
  }
  if ( (p = strchr(mapent,'\n')) ) *p = '\0';

  syslog(LOG_DEBUG, MODPREFIX "%s -> %s", name, mapent);

  return ctxt->parse->parse_mount(root,name,name_len,mapent,
				  ctxt->parse->context); 
}

int lookup_done(void *context)
{
  struct lookup_context *ctxt = (struct lookup_context *) context;
  int rv = close_parse(ctxt->parse);
  free(ctxt);
  return rv;
}
