#!/bin/sh
#-*- Tcl -*-\
    exec tclsh $0 "$@"

#------------------------------------------------------------
#
# Copyright (C) 2000 Mission Critical Linux
#
# All Rights Reserved. Unpublished rights reserved under the
# copyright laws of the United States. This software and all
# associated intellectual property embodies the confidential
# technology of Mission Critical Linux LLC. Possession, use,
# duplication or dissemination of the software is authorized
# only pursuant to a valid written license from Mission
# Critical Linux LLC.

# Regenerate the cluster.i file from the project header files.  This
# should allow people to change their code with fewer adverse
# consequences to the build.  There is no guarantee, however, that a
# change to a header file will always be "grok'able" to SWIG.
# Eventually, the interfaces should become stable.  Also, there will
# eventually be a more definite list of the functions that need to be
# exported to tcl.  When both of these things happen, the cluster.i
# file should be reduced to only those elements needed in the
# interpreter.
#
# Author: Ron Lawrence (lawrence@missioncriticallinux.com)
# $Revision: 1.29 $
#

# Use this code to regenerate the list of header files:
# set if [open {|find .. -name "*.h" -print} "r"]

# set headers [read $if]

# foreach hf $headers {
#     set ft [file tail $hf]
#     # these headers are problematic:
#     if { -1 == [lsearch -exact {allocate.h hash.h fdlist.h cnxlist.h} $ft] } {
#         set ha($ft) $hf
#     }
# }
# set headers {}
# foreach {hn hp} [array get ha] {
#     lappend headers $hp
# }

# Hard code the headers:

# This is a, more or less, complete list of the headers that are
# interesting to extract entry points and structures from.  Most of
# them aren't needed for cluadmin.

# set headers {
#     ../../include/svcmgr.h
#     ../clulib/clucfg_if.h
#     ../daemons/svcmgr_proto.h
#     ../../include/power.h
#     ../../include/disk_proto.h
#     ../../include/diskapis.h
#     ../../include/logger.h
#     ../../include/msgsvc.h
#     ../../include/clu_lock.h
#     ../../include/clucfg.h
#     ../../include/clusterdefs.h
#     ../../include/cluadmin.h
#     ../../include/parseconf.h
# }

# This is a shorter list of headers, including only those that are
# absolutely needed to make cluadmin go.

set headers {
    ../../include/disk_proto.h
    ../../include/svcmgr.h
    ../../include/clusterdefs.h
    ../../include/parseconf.h
}

if { [file exists "../../include/svcmgr_convolo.h"] } {
    lappend headers "../../include/svcmgr_convolo.h"
}


#close $if

# this is the code that precedes the headers included in the .i file:
set prelude {
%module cluster
%{
#include <clu_lock.h>
#include <cluadmin.h>
#include <clucfg.h>
#include <clusterdefs.h>
#include <diskapis.h>
#include <diskstate.h>
#include <logger.h>
#include <msgsvc.h>
#include <net/if.h>
#include <netinet/in.h>
#include <parseconf.h>
#include <power.h>
#include <stdio.h>
#include <stdlib.h>
#include <svcmgr.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>

int loglevel=1;
int myNodeID;
msg_handle_t listen_fd=-1;

char *myNodeName;
%}
%include pointer.i
%pragma make_default

%inline %{

  char** new_charpp () {
    return (char **)malloc(sizeof(char**));
  }
  char* cfg_deref(char **cpp) {
    return *cpp;
  }
  void delete_charpp(char **cpp) {
    free(cpp);
  }
  
  struct CFG_Iter** new_iterpp () {
    static struct CFG_Iter* iter; 
    iter = (struct CFG_Iter*)malloc(sizeof(struct CFG_Iter*));
    return &iter;
  }
  struct CFG_Iter* cfg_deref_iter (struct CFG_Iter**ptr) {
    return *ptr;
  }
  void delete_iterpp (struct CFG_Iter** iterpp) {
    free(iterpp);
  }

  char * cbuffer_new(int size) {
      return (char *)malloc(size);
  }
  int cbuffer_clear(char *buffer, int size) {
      memset(buffer,0,size);
      return 1;
  }
  void cbuffer_delete(char *buffer) {
      free(buffer);
  }
  int getNodeState(int nodeNum) {
      return 0;
  }
  char *getenv(const char *name);
  static void ignore_signals() {
      signal(SIGQUIT, SIG_IGN);
      signal(SIGINT,  SIG_IGN);
  }
  int cluConfigChangeNotification(void);
  int clu_set_loglevel(int severity);

  #define FALSE 0
  #define TRUE  1
  int noNonDisabledServicesOtherNode() {
    CluCfg		*cfg;
    int id;
    SharedDiskNodeStates nodestates;
    int retval;

    cfg = get_clu_cfg(NULL);
    if(NULL == cfg) {
      return FALSE;
    }
    id = cluGetLocalNodeId();
    retval = cluGetDiskNodeStates(&nodestates);
    if(retval < 0) {
      free(cfg);
      return FALSE;
    }
    { int i;                      /* Check each other node */
    for (i=0; i<cfg->num_nodes; ++i) {
      if(i != id && nodestates.states[i] != NODE_DOWN) {
        {int s;
        ServiceBlock	svc;
        for(s=0; s< MAX_SERVICES; ++s) {
          if (getServiceStatus(s, &svc) < 0)
            continue;
          if( svc.owner != id && svc.state != SVC_DISABLED ) {
            free(cfg);
            return FALSE;
          }
        }
        }
      }
    }
    }
    free(cfg);
    return TRUE;
  }
  int allOtherNodesDown() {
    CluCfg		*cfg;
    int id;
    SharedDiskNodeStates nodestates;
    int retval;

    cfg = get_clu_cfg(NULL);
    if(NULL == cfg) {
      return FALSE;
    }
    id = cluGetLocalNodeId();
    retval = cluGetDiskNodeStates(&nodestates);
    if(retval < 0) {
      free(cfg);
      return FALSE;
    }
    { int i;                      /* Check each other node */
    for (i=0; i<cfg->num_nodes; ++i) {
      if(i != id && nodestates.states[i] != NODE_DOWN) {
        free(cfg);
        return FALSE;
      }
    }
    }
    free(cfg);
    return TRUE;
  }
  void detach_completer(void);
  void attach_completer(void);
  void load_history(void);
  void save_history(void);
  void getaline(char* prompt, char *buffer, unsigned int size);

  int ensure_running_as_root(void) {
    if (geteuid() != (uid_t)0) {
        fprintf(stderr, "cluadmin must be run as the user root\n");
        exit(1);    
    }
  }

  char * minus_trailing(char *string, int size) {
    int i;
    for (i=size; i>=0; --i) {
      if( string[i] == '\n' ||
          string[i] == '\t' ||
          string[i] == ' ' ||
          !isprint(string[i]) )
        string[i] = '\0';
      else
        break;
    }
    return string;
  }

  int compact_all_services(void);
  int compact_service(int which);

  int cluadmin_getServiceStatus(int svcID) {
      ServiceBlock svcStatus;
      if(SUCCESS == lockAndReqServiceStatus(svcID, &svcStatus)) {
          return svcStatus.state;
      } 
      else {
          return -1;
      }
  }
  int cluadmin_getServiceOwner(int svcID) {
      ServiceBlock svcStatus;
      if(SUCCESS == lockAndReqServiceStatus(svcID, &svcStatus)) {
          return svcStatus.owner;
      } 
      else {
          return -1;
      }
  }
  int cluadmin_getLocalNodeID() {
      char *name;
      return cluGetLocalNodeId();
  }
  int cluadmin_getNodeName(int id, char* name) {
      char *n;
      getNodeName(id,&n);
      strncpy(name, n, 1022);
      return 0;
  }
%}
}

set out [open "cluster.i" "w+"]

puts $out $prelude

set dont_print 0
set cplusplus 0
# read each header, and filter out all of the crap that 1. we don't
# need, and 2. that makes the procedure fail.
foreach h $headers {
    puts "header: $h"
    set f [open $h "r"]
    set contents [read $f]
    close $f

    set lines [split $contents "\n"]
    foreach line $lines {
        if { [regexp {\#ifdef __cplusplus} $line] } {
            set cplusplus 1
            continue
        }
        if { $cplusplus && ![regexp "\#endif" $line] } {
            continue
        }
        if { $cplusplus && [regexp "\#endif" $line] } {
            set cplusplus 0
            continue
        }
        if { [regexp {\.\.\.} $line] } {
            continue
        }
        if { [regexp {int print[SN]} $line] } {
            continue
        }
        if { [regexp {enum PWR_boolean { PWR_FALSE, PWR_TRUE };} $line] } {
            continue
        }
        if { [regexp {enum PWR_boolean} $line] } {
            regsub -all -- {enum PWR_boolean} $line {int} line
        }
        if { [regexp {.ifdef SERVICE_TESTING} $line] } {
            set dont_print 1
        }
        if { [regexp {.endif SERVICE_TESTING} $line] } {
            set dont_print 0
            continue
        }
        if { [regexp "DEFAULT_LOGLEVEL\[ \t\]+LOG_NOTICE" $line] } {
            continue
        }
        if { [regexp "BOUNCEIO_READ|BOUNCEIO_WRITE" $line] } {
            continue
        }
        if { [regexp "LOGLEVEL_DFLT" $line] } {
            continue
        }
        if { [regexp {	struct msg .} $line] } {
            puts $out "struct msgg \{"
            continue
        }
        if { [regexp {MAX_IPC_MSGLEN} $line] } {
            continue
        }
        if { [regexp {int cnx_add_conn} $line] ||
             [regexp {int cnx_del_conn} $line] } {
            continue
        }
        if { [regexp {printLockBlocks|printPingerStats|startPinger|stopPinger|printQuorumStats|copyService|stopQuorumd|startQuorumd|findNextFreeService|clu_lock_held_check|cluModifyNodeName|myNodeName|clu_register_hb_func} $line] } {
            continue
        }

        if { [regexp "^struct\[ \t\]+fd_list(_head)?" $line m] } {
            puts stderr "matched $m"
            set $dont_print 1
        }
        if { $dont_print &&
             [regexp "^\};$" $line] } {
            set $dont_print 0
            continue
        }
        if { ! $dont_print } {
            puts $out $line
        }
    }
}

close $out

