
/******************************************************************
* 
* Filename: libipmi_session.c
*
* Description: Contains implementation of session establishment 
*   functions to BMC
*
* Author: Jimbo
*
******************************************************************/
#include <stdlib.h>
#include <stdint.h>
#include "libipmi_session.h"
//#include "libipmi_rmcp.h"
#include "libipmi_IPM.h"
//#include "libipmi_AMIOEM.h"
#include <string.h>
//#include "dbgout.h"
//#include "libipmi_sal_session.h"
#include <stdio.h>
#include "com_IPMI_App.h"
#include "com_IPMI_AppDevice.h"
#include "com_IPMIDefs.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#define MAX_RETRIES 10

uint16_t LIBIPMI_CreateSession(IPMI20_UDS_SESSION_T *pUDSSession, int timeout)
{
    uint16_t wRet = LIBIPMI_E_SUCCESS;
    int client_sock;
    struct sockaddr_un client_addr;

    printf("---> Create_IPMI20_UDS_Session\n");
    
    /* Open the socket */
    client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if(client_sock == -1)
    {
        printf("LIBIPMI_UDS_SESSION.C: Unable to create socket \n");
        return LIBIPMI_MEDIUM_E_INVALID_SOCKET;
    }

    int opt = 1;
    setsockopt(client_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    /*Connect the client to server*/
    bzero(&client_addr, sizeof(client_addr));
    client_addr.sun_family = AF_UNIX;
    strcpy(client_addr.sun_path,UDS_SOCKET_PATH);
    int len = sizeof(client_addr.sun_family)+strlen(client_addr.sun_path);

    printf("---> UDS_Connect, socket: %d, SocketPath: %s\n", client_sock, UDS_SOCKET_PATH);
    if ( connect (client_sock, (struct sockaddr *) &client_addr,len) < 0)
    {
        printf("LIBIPMI_UDS_SESSION.C: Unable to connect to Server:\n");
        return LIBIPMI_MEDIUM_E_CONNECT_FAILURE;
    }

    pUDSSession->hUDSInfo.hSocket = client_sock;
    pUDSSession->hUDSInfo.byIsConnected = 1;
    pUDSSession->byDefTimeout = timeout;
    pUDSSession->dwSessionID = 0;
    pUDSSession->byChannelNum = UDS_CHANNEL;

    return wRet;
}


/*This Functions Fills the Packet
 * with the valid fields for UDS*/
uint32_t FillUDSMessagePacket(IPMI20_UDS_SESSION_T *pUDSSession, uint8_t *Buffer,
                        uint8_t byNetFnLUN, uint8_t byCommand,
                        uint8_t *pbyReqData, uint32_t dwReqDataLen)
{
    uint8_t *pbyIPMIUDSData;
    IPMIUDSMsg_T *pUDSMsg;
    pUDSMsg = (IPMIUDSMsg_T *)&Buffer[0];
    pbyIPMIUDSData = &Buffer[sizeof(IPMIUDSMsg_T)];

    pUDSMsg->SessionID = pUDSSession->dwSessionID;
    pUDSMsg->ChannelNum = pUDSSession->byChannelNum;
    //pUDSMsg->Privilege = pUDSSession->byRole;
    pUDSMsg->IPMIMsgLen = sizeof(IPMIUDSMsg_T)+dwReqDataLen+1;
    pUDSMsg->Cmd = byCommand;
    pUDSMsg->NetFnLUN = byNetFnLUN;

    //memcpy(pUDSMsg->UserName, pUDSSession->szUName, MAX_USERNAME_LEN);
    //memcpy(pUDSMsg->IPAddr, pUDSSession->abyIPAddr, IP6_ADDR_LEN);
    //pUDSMsg->ProcessID = getpid();
    //pUDSMsg->ThreadID = gettid();
    memcpy(pbyIPMIUDSData, pbyReqData, dwReqDataLen);

    return ( sizeof(IPMIUDSMsg_T) + dwReqDataLen + 1);
}

/*---------------------------------------------------------------------
LIBIPMI_Send_RAW_IPMI2_0_Command

Sends RAW IPMI Command to BMC
----------------------------------------------------------------------*/
uint16_t LIBIPMI_Send_RAW_IPMI2_0_Command(IPMI20_UDS_SESSION_T *pUDSSession,        //notice!
                                    uint8_t byNetFnLUN, uint8_t byCommand,
                                    uint8_t *pszReqData, uint32_t dwReqDataLen,
                                    uint8_t *pszResData, uint32_t *pdwResDataLen,
                                    int timeout)
{
    uint8_t pbyIPMIUDSMsgReq[MAX_REQUEST_SIZE];
    uint8_t pbyIPMIUDSResData[MAX_RESPONSE_SIZE];
    int retlen=0,byretval=0;
    int i;
    fd_set  fds;
    struct timeval Timeout;
    int max = 0;

    retlen = FillUDSMessagePacket(pUDSSession,pbyIPMIUDSMsgReq,byNetFnLUN,byCommand,pszReqData,dwReqDataLen);

    if(retlen <= 0 )
    {
       printf("Data Length Invalid\n");
       return -1;
    }

    send (pUDSSession->hUDSInfo.hSocket,pbyIPMIUDSMsgReq,retlen, MSG_NOSIGNAL);

    if((byNetFnLUN == NETFNLUN_IPMI_APP) && (byCommand == CMD_SEND_MSG))
    {
        Timeout.tv_sec  =   timeout; 
        Timeout.tv_usec  =   0; 

        FD_ZERO(&fds);      
        FD_SET(pUDSSession->hUDSInfo.hSocket,&fds);        
        max = pUDSSession->hUDSInfo.hSocket+1;

        /*Waits for an event to occur on socket*/
        byretval = select (max, &fds, NULL, NULL, &Timeout);
        if (-1 == byretval)
        {
            printf("UDS Recv1 error!\n");
            return -1;
        }
        if (0 == byretval)
        {
            /* Its due to timeout - continue */
            printf("UDS Recv1 timeout\n");
            return -1;
        }
            
        /*Accepting Connection*/
        if(FD_ISSET(pUDSSession->hUDSInfo.hSocket,&fds))
        {
            byretval = recv(pUDSSession->hUDSInfo.hSocket,pbyIPMIUDSResData,MAX_RESPONSE_SIZE,0);
            // printf("UDS Recv1 %d: ", byretval);
            // for(i=0;i<byretval;i++)
            //     printf("%#02x ", pbyIPMIUDSResData[i]);
            // printf("\n");
        }
    }

    Timeout.tv_sec  =   timeout; 
    Timeout.tv_usec  =   0; 

    FD_ZERO(&fds);      
    FD_SET(pUDSSession->hUDSInfo.hSocket,&fds);        
    max = pUDSSession->hUDSInfo.hSocket+1;

    /*Waits for an event to occur on socket*/
    byretval = select (max, &fds, NULL, NULL, &Timeout);
    if (-1 == byretval)
    {
        printf("UDS Recv2 error!\n");
        return -1;
    }
    if (0 == byretval)
    {
        /* Its due to timeout - continue */
        printf("UDS Recv2 timeout\n");
        return -1;
    }
        
    /*Accepting Connection*/
    if(FD_ISSET(pUDSSession->hUDSInfo.hSocket,&fds))
    {
        byretval = recv(pUDSSession->hUDSInfo.hSocket,pbyIPMIUDSResData,MAX_RESPONSE_SIZE,0);
        // printf("UDS Recv2 %d: ", byretval);
        // for(i=0;i<byretval;i++)
        //     printf("%#02x ", pbyIPMIUDSResData[i]);
        // printf("\n");
    }

    if(byretval > 0)
    {
        IPMIUDSMsg_T *pIPMIUDSMsg = (IPMIUDSMsg_T *)&pbyIPMIUDSResData[0];        
        *pdwResDataLen = pIPMIUDSMsg->IPMIMsgLen-sizeof(IPMIUDSMsg_T) - 1;
        memcpy(pszResData,&pbyIPMIUDSResData[sizeof(IPMIUDSMsg_T)],*pdwResDataLen);
    }
    else
        return STATUS_CODE(IPMI_ERROR_FLAG, byretval);

    return LIBIPMI_STATUS_SUCCESS;
}

/*---------------------------------------------------------------------
LIBIPMI_CloseSession

Closes the connection with BMC
----------------------------------------------------------------------*/
uint16_t LIBIPMI_CloseSession( IPMI20_UDS_SESSION_T *pUDSSession )
{
    printf("LIBIPMI_CloseSession %d\n", pUDSSession->hUDSInfo.hSocket);
    close(pUDSSession->hUDSInfo.hSocket);
    /* reset the connected flag */
    pUDSSession->hUDSInfo.byIsConnected = 0;
    return LIBIPMI_STATUS_SUCCESS;
}

// /*---------------------------------------------------------------------
// SetPrivilegeLevel

// Sets the session privilege to specified level
// ----------------------------------------------------------------------*/
// uint16_t SetPrivilegeLevel(IPMI20_SESSION_T *pSession, uint8_t *byPrivLevel, int timeout)
// {
//  uint8_t         byReq;
//  uint8_t         pbyRes[2];
//  uint32_t            dwResLen;
//  uint16_t            wRet;


//  printf("---> SetPrivilegeLevel\n");
//  byReq = *byPrivLevel;
//  wRet = LIBIPMI_Send_RAW_IPMI2_0_Command (pSession,DEFAULT_NET_FN_LUN,
//                                              CMD_SET_SESSION_PRIV_LEVEL,
//                                              &byReq, sizeof(uint8_t),
//                                              &pbyRes[0], &dwResLen, timeout);


//  if(wRet != 0)
//  {
//      printf("Originally requested priv level failed\n");
//      //find the current privilege level allowed
//      byReq = 0;
//      wRet = LIBIPMI_Send_RAW_IPMI2_0_Command (pSession, DEFAULT_NET_FN_LUN,
//                                              CMD_SET_SESSION_PRIV_LEVEL,
//                                              &byReq, sizeof(uint8_t),
//                                              &pbyRes[0], &dwResLen, timeout);
//      if(wRet != 0)
//      {
//          printf("Amazing..request for current privilege limit returned an error %x2\n",wRet);
//      }
//      else
//      {
//          printf("New priv level is %x\n",pbyRes[1]);
//          *byPrivLevel = pbyRes[1];
//      }
//  }

//  return wRet;
// }

