/*
 * mount_nasd.c
 *
 * mount assist for nasd
 *
 * Author: Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_edrfs_client.h>
#include <nasd/linux/nasd_edrfs_client_linux_mount.h>

#include <sys/mount.h>

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

#define FSTYPE "edrfs"



void parse_spec(nasd_edrfs_mount_data_t   *mount_data,
		int                      argc,
		char                   **argv);

void parse_options(nasd_edrfs_mount_data_t   *mount_data,
		   int                      argc,
		   char                   **argv);

void set_flag(nasd_edrfs_mount_data_t  *mount_data,
	      char                   *option);

int main(int argc, char **argv) {  
  nasd_edrfs_mount_data_t mount_data;
  int err = 0;
  int mount_flags;
  FILE *mtabfile = NULL;
  struct mntent mntent;

  if (argc < 5) {
    nasd_printf("I don't have enough arguments. bye.\n");
    return -1;
  }

  /* set up defaults */
  mount_data.version = NASD_EDRFS_MOUNT_VERSION;
  mount_data.flags = NASD_EDRFS_MOUNT_RW;

#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_NONE
  /* in this case, the default userland package is MSGQ, while the
     default kernel package is COLOCATE. so if we use the default,
     we'll get hurt. feel the love. */
  mount_data.binding_type = NASD_BIND_COLOCATE;
#else /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_NONE */
  mount_data.binding_type = NASD_BIND_DEFAULT;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_NONE */

  /* parse spec and options */
  parse_spec(&mount_data, argc, argv);
  parse_options(&mount_data, argc, argv);

  mount_flags =  (MS_MGC_VAL | MS_NODEV);
  mount_flags |= ((mount_data.flags && NASD_EDRFS_MOUNT_RW) ? 0 : MS_RDONLY);

  /* do the actual mount */
  err = mount(argv[1], mount_data.mountpoint, NASD_EDRFS_FS_IDENTIFIER,
	      mount_flags, (void *) &mount_data);

  if (err != 0) {
    perror("EDRFS mount failed");
    exit(1);
  }

  /* now we must mangle mtab */

  /* set up the mntent.
     argv[1] is the fsname.
     argv[2] is the mountpoint.
     argv[4] is the option string.
     FSTYPE is the fs type.
  */
  
  NASD_Malloc(mntent.mnt_fsname, strlen(argv[1]) + 1, (char *));
  NASD_Malloc(mntent.mnt_dir,    strlen(argv[2]) + 1, (char *));
  NASD_Malloc(mntent.mnt_opts,   strlen(argv[4]) + 1, (char *));
  NASD_Malloc(mntent.mnt_type,   strlen(FSTYPE)  + 1, (char *));

  strncpy(mntent.mnt_fsname, argv[1], strlen(argv[1]) + 1);
  strncpy(mntent.mnt_dir,    argv[2], strlen(argv[2]) + 1);
  strncpy(mntent.mnt_opts,   argv[4], strlen(argv[4]) + 1);
  strncpy(mntent.mnt_type,   FSTYPE,  strlen(FSTYPE)  + 1);

  mntent.mnt_freq = 0;
  mntent.mnt_passno = 0;

  mtabfile = setmntent(MOUNTED, "a+");
  if (mtabfile == NULL) {
    perror("Can't open " MOUNTED);
    exit(1);
  }

  err = addmntent(mtabfile, &mntent);
  err = endmntent(mtabfile);

  NASD_Free(mntent.mnt_fsname, strlen(argv[1]) + 1);
  NASD_Free(mntent.mnt_dir,    strlen(argv[2]) + 1);
  NASD_Free(mntent.mnt_opts,   strlen(argv[4]) + 1);
  NASD_Free(mntent.mnt_type,   strlen(FSTYPE)  + 1);

  exit(err);
}


/* split mount spec into host and partition, find mountpoint */
void parse_spec(nasd_edrfs_mount_data_t *mount_data, int argc, char **argv) {
  char *tmp_str = NULL, *tmp_part = NULL;
  struct hostent host;
  nasd_status_t rc;
  int len = strlen(argv[1]) + 1;

  NASD_Malloc(tmp_str, len, (char *));
  if (tmp_str == NULL) {
    nasd_printf("Can't allocate memory, bailing!\n");
    exit(1);
  }
  
  strncpy(tmp_str, argv[1], len);

  tmp_part = strchr(tmp_str, ':');
  
  if (tmp_part == NULL) {
    nasd_printf("Invalid mount spec '%s'!\n", tmp_str);
    exit(1);
    }
  
  *tmp_part++ = '\0';
  
  strncpy(mount_data->server, tmp_str, NASD_EDRFS_MAX_HOSTLEN);
  strncpy(mount_data->partition, tmp_part, NASD_EDRFS_MAX_PATHLEN);
  strncpy(mount_data->mountpoint, argv[2], NASD_EDRFS_MAX_PATHLEN);

  NASD_Free(tmp_str, len);

  /* god I hate this part */
  rc = nasd_gethostbyname_r(mount_data->server, &host);

  if (rc != NASD_SUCCESS) {
    fprintf(stderr, "Uh-oh. nasd_gethostbyname_r failed! rc=0x%x (%s)\n",
	    rc, nasd_error_string(rc));
    exit(1);
  }

  /* we take the first address. */
  if (host.h_length < 1) {
    fprintf(stderr, "That host has no addresses! I can't do anything with it.\n");
    exit(1);
  }
  
  sprintf(mount_data->server_ip, "%u.%u.%u.%u",
	  (unsigned char) host.h_addr_list[0][0],
	  (unsigned char) host.h_addr_list[0][1],
	  (unsigned char) host.h_addr_list[0][2],
	  (unsigned char) host.h_addr_list[0][3]);
}


/* parse mount options */
void parse_options(nasd_edrfs_mount_data_t *mount_data, int argc, char **argv) {
  char *tmp_str = NULL, *tmp_opt = NULL;
  int len;
  
  if (strcmp("-o", argv[3])) { /* sanity check */
    nasd_printf("I don't see my options where I expected. uh-oh.\n");
    exit(1);
  }

  len = strlen(argv[4]) + 1;

  NASD_Malloc(tmp_str, len, (char *));
  if (tmp_str == NULL) {
    nasd_printf("Couldn't allocate memory!\n");
    exit(1);
  }

  strcpy(tmp_str, argv[4]);

  tmp_opt = strtok(tmp_str, ",");

  while (tmp_opt != NULL) {
    set_flag(mount_data, tmp_opt);
    tmp_opt = strtok(NULL, ",");
  }

  NASD_Free(tmp_str, len);
}


void set_flag(nasd_edrfs_mount_data_t *mount_data, char *option) {
  if (!strcmp("rw", option)) {
    mount_data->flags |= NASD_EDRFS_MOUNT_RW;
  } else if (!strcmp("ro", option)) {
    mount_data->flags &= ~NASD_EDRFS_MOUNT_RW;
  } else if (!strcmp("serverparse", option)) {
    mount_data->flags |= NASD_EDRFS_MOUNT_SERVERPARSE;
  } else if (!strcmp("colocate", option)) {
    mount_data->binding_type = NASD_BIND_COLOCATE;    
  } else if (!strcmp("msgq", option)) {
    mount_data->binding_type = NASD_BIND_MSGQ;    
  } else if (!strcmp("srpc", option)) {
    mount_data->binding_type = NASD_BIND_SRPC;    
  } else {
    fprintf(stderr, "Hey! I don't know what to do with a '%s' option.\n",
	    option);
    exit(1);
  }
}
