/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* midnightcode-lvm.c :: Midnight Code Logical Volume Manager library	*/


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

#include "midnightcode-list.h"
#include "midnightcode-parse.h"
#include "midnightcode-process.h"
#include "midnightcode-disk.h"
#include "midnightcode-lvm.h"


int
midnightcode_lvm_pvcreate(struct midnightcode_disk_head_t *elements) {
  int options;
  int terminal;
  int ret_i;
  int diskn;
  struct midnightcode_disk_element_t * disk;
  char * disk_buffer;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  if((disk_buffer = malloc(1024)) < 0)
    return 1;

  memset(disk_buffer, 0, 1024);

  midnightcode_list_for_each(disk, elements) {
    if(access(disk->device, F_OK))
      return 0 - diskn;

    if((fd = open(disk->device, O_DIRECT | O_WRONLY)) < 0)
      return 0 - diskn;

    if(write(fd, disk_buffer, 1024) < 0)
      return 0 - diskn;

    close(fd);

    memset(execute, 0, sizeof(execute));
    ret_i = snprintf(execute,  sizeof(execute) - 1,
                     "pvcreate %s", disk->device);
    if(ret_i >= (int)sizeof(execute))
      return 0 - diskn;

    ret_i = midnightcode_process(execute, options, terminal);
    if(ret_i != 0)
      return 0 - diskn;

    diskn++;
  }

  if(diskn < 1)
    return 1;

  /* TODO: return or manage the exit code from pvcreate */

  return 0;
}


int
midnightcode_lvm_vgcreate(char * vg_label,
                          struct midnightcode_disk_head_t *elements) {
  int options;
  int terminal;
  int ret_i;
  int diskn;
  struct midnightcode_disk_element_t * disk;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgcreate --maxphysicalvolumes 32 "
                   "--physicalextentsize 256M %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  midnightcode_list_for_each(disk, elements) {
    if(!strncat(execute, " ", sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;
    if(!strncat(execute, disk->device, sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;

    diskn++;
  }

  if(diskn < 1)
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgcreate */

  return 0;
}


int
midnightcode_lvm_vgactivate(char * vg_label) {
  int options;
  int terminal;
  int ret_i;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgchange --available y %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgchange */

  return 0;
}


int
midnightcode_lvm_vgremove(char * vg_label) {
  int options;
  int terminal;
  int ret_i;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgchange --available n %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgchange */

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgremove %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgremove */

  return 0;
}


int
midnightcode_lvm_vgextend(char * vg_label,
                          struct midnightcode_disk_head_t *elements) {
  int options;
  int terminal;
  int ret_i;
  int diskn;
  struct midnightcode_disk_element_t * disk;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgextend %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  midnightcode_list_for_each(disk, elements) {
    if(!strncat(execute, " ", sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;
    if(!strncat(execute, disk->device, sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;

    diskn++;
  }

  if(diskn < 1)
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgextend */

  return 0;
}



int
midnightcode_lvm_vgreduce(char * vg_label,
                          struct midnightcode_disk_head_t *elements) {
  int options;
  int terminal;
  int ret_i;
  int diskn;
  struct midnightcode_disk_element_t * disk;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;
  diskn = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "vgreduce %s", vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  midnightcode_list_for_each(disk, elements) {
    if(!strncat(execute, " ", sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;
    if(!strncat(execute, disk->device, sizeof(execute) - strlen(execute) - 1))
      return 0 - diskn;

    diskn++;
  }

  if(diskn < 1)
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from vgreduce */

  return 0;
}


int
midnightcode_lvm_lvcreate(char * vg_label, char * lv_label, int lv_size) {
  int options;
  int terminal;
  int ret_i;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "lvcreate --size %dG --name %s %s",
                   lv_size, lv_label, vg_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from lvcreate */

  return 0;
}


int
midnightcode_lvm_lvremove(char * lv_label) {
  int options;
  int terminal;
  int ret_i;
  char execute[4096];


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;
  ret_i = 0;

  memset(execute, 0, sizeof(execute));
  ret_i = snprintf(execute,  sizeof(execute) - 1,
                   "lvremove --force %s", lv_label);
  if(ret_i >= (int)sizeof(execute))
    return 1;

  ret_i = midnightcode_process(execute, options, terminal);
  if(ret_i != 0)
    return 1;

  /* TODO: return or manage the exit code from lvremove */

  return 0;
}




int
midnightcode_lvm_bycommand(struct midnightcode_lvm_command_t * commands) {
  int options;
  int terminal;
  int ret_i;
  int cmdn;
  struct midnightcode_lvm_command_t * cmd;


  options = 0;
  options |= MIDNIGHTCODE_PROCESS_OPTION_WAITTERM;
  options |= MIDNIGHTCODE_PROCESS_OPTION_NULLIO;
  options |= MIDNIGHTCODE_PROCESS_OPTION_EXITCODE;
  terminal = MIDNIGHTCODE_PROCESS_TERMINAL_NULL;

  ret_i = 0;
  for(cmd = commands, cmdn = 0; cmd[cmdn].exec != NULL; cmdn++) {
    ret_i = midnightcode_process(cmd[cmdn].exec, options, terminal);
    if(ret_i != 0) {
      ret_i = 0 - cmdn;
      break;
    }
  }
  if(ret_i == 0)
    ret_i = cmdn;


  return ret_i;
}


int
midnightcode_lvm(int action, char * vg_label, char * lv_label, int lv_size,
                    struct midnightcode_disk_head_t * disk_elements,
                    struct midnightcode_lvm_head_t * lvm_elements) {
  int ret_i;
  struct midnightcode_filter_rule_element_t * rule_element;
  struct midnightcode_filter_chain_element_t * chain_element;

  struct midnightcode_lvm_command_t lvm_shutdown[] = {
    { "vgchange --available n" },
    { 0 }
  };

  struct midnightcode_lvm_command_t lvm_initialise[] = {
    { "dmsteup mknodes" },
    { "vgscan --ignorelockingfailure" },
    { "vgchange --available y --ignorelockingfailure" },
    { 0 }
  };


  ret_i = MIDNIGHTCODE_LVM_ERROR_NONE;

  switch(action) {
    case MIDNIGHTCODE_LVM_ACTION_INITIALISE:
      if(midnightcode_lvm_bycommand(lvm_initialise) < 0)
        ret_i = MIDNIGHTCODE_LVM_ERROR_INITIALISE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_PV_CREATE;
      if(midnightcode_lvm_pvcreate(disk_elements) < 0)
        ret_i = MIDNIGHTCODE_LVM_ERROR_PV_CREATE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_VG_CREATE;
      if(!strlen(vg_label) ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_VG_CREATE;
      } else {
        if(midnightcode_lvm_vgcreate(vg_label, disk_elements) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_VG_CREATE;
      }
      break;

    case MIDNIGHTCODE_LVM_ACTION_VG_ACTIVATE;
      if(!strlen(vg_label) ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_VG_ACTIVATE;
      } else {
        if(midnightcode_lvm_vgactivate(vg_label) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_VG_ACTIVATE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_VG_REMOVE;
      if(!strlen(vg_label) ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_VG_REMOVE;
      } else {
        if(midnightcode_lvm_vgremove(vg_label) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_VG_REMOVE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_VG_EXTEND;
      if(!strlen(vg_label) ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_VG_EXTEND;
      } else {
        if(midnightcode_lvm_vgextend(vg_label, disk_elements) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_VG_EXTEND;
      break;

    case MIDNIGHTCODE_LVM_ACTION_VG_REDUCE;
      if(!strlen(vg_label) ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_VG_REDUCE;
      } else {
        if(midnightcode_lvm_vgreduce(vg_label, disk_elements) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_VG_REDUCE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_LV_CREATE;
      if(!strlen(vg_label) || !strlen(lv_label) || lv_size < 1 ||
         midnightcode_parse_ascii(vg_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) || 
         midnightcode_parse_ascii(lv_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_LV_CREATE;
      } else {
        if(midnightcode_lvm_lvcreate(vg_label, lv_label, lv_size) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_LV_CREATE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_LV_REMOVE;
      if(!strlen(lv_label) ||
         midnightcode_parse_ascii(lv_label,
         MIDNIGHTCODE_PARSE_CHARSET_ALPHANUMERIC) {
        ret_i = MIDNIGHTCODE_LVM_ERROR_LV_REMOVE;
      } else {
        if(midnightcode_lvm_lvremove(lv_label) < 0)
          ret_i = MIDNIGHTCODE_LVM_ERROR_LV_REMOVE;
      break;

    case MIDNIGHTCODE_LVM_ACTION_LV_EXTEND;
      /* TODO: Implement lvextend when FS details are set */
      break;

    case MIDNIGHTCODE_LVM_ACTION_LV_REDUCE;
      /* TODO: Implement lvreduce when FS details are set */
      break;

    case MIDNIGHTCODE_LVM_ACTION_SHUTDOWN:
      if(midnightcode_lvm_bycommand(lvm_shutdown) < 0)
        ret_i = MIDNIGHTCODE_LVM_ERROR_SHUTDOWN;
      break;

    default:
      ret_i = MIDNIGHTCODE_FILTER_ERROR_ACTION;
      break;

  }

  return ret_i;
}

// http://linuxvm.org/Info/HOWTOs/lvm.html
