libpcp.c revision 56b04ea668163bd43a8a0560b2c2df82e3330823
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Platform Channel Protocol Library functions on Nigara platforms
* (Ontario, Erie, etc..) Solaris applications use these interfaces
* to communicate with entities that reside on service processor.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <inttypes.h>
#include <umem.h>
#include <strings.h>
#include <time.h>
#include "libpcp.h"
#include "pcp_common.h"
/*
* Following libpcp interfaces are exposed to user applications.
*
* int pcp_init(char *channel_name);
* int pcp_send_recv(int channel_fd, pcp_msg_t *req_msg, pcp_msg_t *resp_msg,
* uint32_t timeout);
* int pcp_close(int channel_fd);
*
*/
/*
* Forward declarations.
*/
static uint32_t pcp_get_xid(void);
static int pcp_frame_error_handle(void);
int *ispresent);
static int pcp_cleanup(int channel_fd);
/*
* local channel (glvc) file descriptor set by pcp_send_recv()
*/
static int chnl_fd = -1;
/*
* Message Transaction ID
*/
/*
* Channel MTU size.
*/
static unsigned int mtu_size = PCPL_DEF_MTU_SZ;
/*
* timeout field is supplied by user. timeout field is used to decide
* how long to block on glvc driver calls before we return timeout error
* to user applications.
*
* Note: In the current implementation of glvc driver, all glvc calls are
* blocking.
*/
static uint32_t glvc_timeout = 0;
/*
* variables used by setsetjmp/siglongjmp.
*/
static volatile sig_atomic_t jumpok = 0;
static sigjmp_buf jmpbuf;
/*
* To unblock SIGALRM signal incase if it's blocked in libpcp user apps.
* Restore it to old state during pcp_close.
*/
/*
* Buffers used for stream reading channel data. When data is read in
* stream fashion, first data is copied from channel (glvc) buffers to
* these local buffers from which the read requests are serviced.
*/
/*
* Buffer used for peeking new data available in channel (glvc) buffers.
*/
#define PEEK_AREA_SIZE (mtu_size)
/*
* Buffers used for peeking data available either in local buffers or
* new data available in channel (glvc) buffers.
*/
static int req_msg_hdr_sz = 0;
static int resp_msg_hdr_sz = 0;
/*
* signal handling variables to handle glvc blocking calls.
*/
/* To restore old SIGALRM signal handler */
static void
glvc_timeout_handler(void)
{
if (jumpok == 0)
return;
}
/*
* Initialize the virtual channel. It basically opens the virtual channel
* provided by the host application.
*
*/
int
pcp_init(char *channel_name)
{
int channel_fd;
if (channel_name == NULL)
return (PCPL_INVALID_ARGS);
/*
* Open virtual channel name.
*/
return (PCPL_GLVC_ERROR);
}
/*
* Get the Channel MTU size
*/
(void) close(channel_fd);
return (PCPL_GLVC_ERROR);
}
/*
* Get current signal mask. If SIGALRM is blocked
* unblock it.
*/
(void) sigemptyset(&blkset);
}
/*
* signal handler initialization to handle glvc call timeouts.
*/
(void) close(channel_fd);
return (PCPL_ERROR);
}
return (channel_fd);
}
/*
* Function: Close platform channel.
* Arguments:
* int channel_fd - channel file descriptor.
* Returns:
* always returns PCPL_OK for now.
*/
int
pcp_close(int channel_fd)
{
if (channel_fd >= 0) {
(void) pcp_cleanup(channel_fd);
(void) close(channel_fd);
} else {
return (-1);
}
/*
* free global buffers
*/
}
}
if (peek_read_area != NULL) {
}
if (req_msg_hdr != NULL) {
req_msg_hdr = NULL;
}
if (resp_msg_hdr != NULL) {
resp_msg_hdr = NULL;
}
/*
* Restore SIGALRM signal mask incase if we unblocked
* it during pcp_init.
*/
}
/* Restore SIGALRM signal handler */
return (PCPL_OK);
}
/*
* Function: Send and Receive messages on platform channel.
* Arguments:
* int channel_fd - channel file descriptor.
* pcp_msg_t *req_msg - Request Message to send to other end of channel.
* pcp_msg_t *resp_msg - Response Message to be received.
* uint32_t timeout - timeout field when waiting for data from channel.
* Returns:
* 0 - success (PCPL_OK).
* (-ve) - failure:
* PCPL_INVALID_ARGS - invalid args.
* PCPL_GLVC_TIMEOUT - glvc call timeout.
* PCPL_XPORT_ERROR - transport error in request message
* noticed by receiver.
* PCPL_MALLOC_FAIL - malloc failure.
* PCPL_CKSUM_ERROR - checksum error.
*/
int
{
void *datap;
void *resp_msg_data = NULL;
int ret;
int resp_hdr_ok;
#ifdef PCP_CKSUM_ENABLE
#endif
if (channel_fd < 0) {
return (PCPL_ERROR);
}
/* copy channel_fd to local fd (chnl_fd) for other functions use */
return (PCPL_INVALID_ARGS);
}
if (timeout > 0)
else
glvc_timeout = 0;
return (PCPL_INVALID_ARGS);
if (req_msg_hdr == NULL) {
req_msg_hdr_sz = sizeof (pcp_req_msg_hdr_t);
if (req_msg_hdr == NULL)
return (PCPL_MALLOC_FAIL);
}
/* calculate request msg_cksum */
}
/*
* Fill in the message header for the request packet
*/
req_msg_hdr->rsvd_pad = 0;
req_msg_hdr->hdr_cksum = 0;
/* fill request header checksum */
/*
* set sig jmp location
*/
return (PCPL_GLVC_TIMEOUT);
}
/*
* send request message header
*/
return (ret);
}
/*
* send request message
*/
PCPL_IO_OP_WRITE))) {
return (ret);
}
}
return (PCPL_OK);
if (resp_msg_hdr == NULL) {
resp_msg_hdr_sz = sizeof (pcp_resp_msg_hdr_t);
if (resp_msg_hdr == NULL)
return (PCPL_MALLOC_FAIL);
}
resp_hdr_ok = 0;
while (!resp_hdr_ok) {
/*
* Receive response message header
* Note: frame error handling is done in
* 'pcp_recv_resp_msg_hdr()'.
*/
return (ret);
}
/*
* Check header checksum if it matches with the received hdr
* checksum.
*/
#ifdef PCP_CKSUM_ENABLE
resp_msg_hdr->hdr_cksum = 0;
if (cksum != bkup_resp_hdr_cksum) {
return (PCPL_CKSUM_ERROR);
}
#endif
/*
* Check for matching request and response messages
*/
continue; /* continue reading response header */
}
resp_hdr_ok = 1;
}
/*
* check status field for any channel protocol errrors
* This field signifies something happend during request
* message trasmission. This field is set by the receiver.
*/
return (PCPL_XPORT_ERROR);
}
if (resp_msg_hdr->msg_len != 0) {
/* libpcp users should free this memory */
== NULL)
return (PCPL_MALLOC_FAIL);
/*
* Receive response message.
*/
PCPL_IO_OP_READ))) {
return (ret);
}
#ifdef PCP_CKSUM_ENABLE
/* verify response message data checksum */
return (PCPL_CKSUM_ERROR);
}
#endif
}
/* Everything is okay put the received data into user */
/* application's resp_msg struct */
return (PCPL_OK);
}
/*
* Function: Get channel property values.
* Arguments:
* int channel_fd - channel file descriptor.
* int prop - property id.
* unsigned int *val - property value tobe copied.
* Returns:
* 0 - success
* (-ve) - failure:
* PCPL_ERR_GLVC - glvc ioctl failure.
*/
static int
{
int ret;
channel_op.opt_val = 0;
(void) alarm(glvc_timeout);
&channel_op)) < 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
return (0);
}
/*
*/
static int
{
int rv;
int n;
int io_sz;
int try_cnt;
return (PCPL_INVALID_ARGS);
}
switch (io_op) {
case PCPL_IO_OP_READ:
break;
case PCPL_IO_OP_WRITE:
break;
case PCPL_IO_OP_PEEK:
break;
default:
return (PCPL_INVALID_ARGS);
}
/*
* loop until all I/O done, try limit exceded, or real failure
*/
rv = 0;
try_cnt = 0;
try_cnt++;
if (try_cnt > PCPL_MAX_TRY_CNT) {
rv = n;
goto done;
}
(void) sleep(PCPL_GLVC_SLEEP);
} /* while trying the io operation */
if (n < 0) {
rv = n;
goto done;
}
rv += n;
datap += n;
} /* while still have more data */
done:
return (0);
else
return (PCPL_GLVC_ERROR);
}
/*
* For peeking 'bytes_cnt' bytes in channel (glvc) buffers.
* If data is available, the data is copied into 'buf'.
*/
static int
{
int ret;
int n, m;
return (PCPL_INVALID_ARGS);
}
/*
* initialization of buffers used for peeking data in channel buffers.
*/
return (PCPL_MALLOC_FAIL);
}
}
/*
* peek max MTU size bytes
*/
(void) alarm(glvc_timeout);
< 0) {
(void) alarm(0);
return (ret);
}
(void) alarm(0);
if (n < 0)
return (PCPL_GLVC_ERROR);
/*
* satisfy request as best as we can
*/
return (m);
}
/*
* Function: write 'byte_cnt' bytes from 'buf' to channel.
*/
static int
{
int ret;
/* check for valid arguments */
return (PCPL_INVALID_ARGS);
}
(void) alarm(glvc_timeout);
(void) alarm(0);
return (ret);
}
(void) alarm(0);
return (ret);
}
/*
* In current implementaion of glvc driver, streams reads are not supported.
* pcp_read mimics stream reads by first reading all the bytes present in the
* channel buffer into a local buffer and from then on read requests
* are serviced from local buffer. When read requests are not serviceble
* from local buffer, it repeates by first reading data from channel buffers.
*
* This call may need to be enhanced when glvc supports buffered (stream)
* reads - TBD
*/
static int
{
int ret;
int n, m, i;
return (PCPL_INVALID_ARGS);
}
/*
* initialization of local read buffer
* from which the stream read requests are serviced.
*/
return (PCPL_MALLOC_FAIL);
}
}
/*
* if we already read this data then copy from local buffer it self
* without calling new read.
*/
return (byte_cnt);
}
/*
* if the request is not satisfied from the buffered data, then move the
* remaining data to front of the buffer and read new data.
*/
}
/*
* do a peek to see how much data is available and read complete data.
*/
return (m);
}
(void) alarm(glvc_timeout);
(void) alarm(0);
return (ret);
}
(void) alarm(0);
/*
* copy the requested bytes.
*/
read_head += n;
return (n);
}
/*
* This function is slight different from pcp_peek. The peek requests are first
* serviced from local read buffer, if data is available. If the peek request
* is not serviceble from local read buffer, then the data is peeked from
* channel buffer. This function is mainly used for proper protocol framing
* error handling.
*/
static int
{
int n, m, i;
return (PCPL_INVALID_ARGS);
}
/*
* initialization of peek_read buffer.
*/
if (peek_read_area == NULL) {
if (peek_read_area == NULL) {
return (PCPL_MALLOC_FAIL);
}
}
/*
* if we already have the data in local read buffer then copy
*/
return (byte_cnt);
}
/*
* if the request is not satisfied from local read buffer, then first
* copy the remaining data in local read buffer to peek_read_area and
* then issue new peek.
*/
peek_read_area[i] = read_head[i];
}
peek_read_tail = peek_read_head + i;
/*
* do a peek to see how much data is available and read complete data.
*/
return (m);
}
peek_read_tail += m;
/*
* copy the requested bytes
*/
return (n);
}
/*
* Send Request Message Header.
*/
static int
{
int hdr_sz;
int ret;
hdr_sz = sizeof (pcp_req_msg_hdr_t);
UMEM_DEFAULT)) == NULL) {
return (PCPL_MALLOC_FAIL);
}
return (ret);
}
return (PCP_OK);
}
/*
* Receive Response message header.
*/
static int
{
int ret;
return (PCPL_INVALID_ARGS);
}
/*
* handle protocol framing errors.
* pcp_frame_error_handle() returns when proper frame arrived
* (magic seq) or if an error happens while reading data from
* channel.
*/
if ((ret = pcp_frame_error_handle()) != 0)
return (PCPL_FRAME_ERROR);
/* read magic number first */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
if (magic_num != PCP_MAGIC_NUM) {
return (PCPL_FRAME_ERROR);
}
/* read version field */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* check protocol version */
if (proto_ver != PCP_PROT_VER_1) {
return (PCPL_PROT_ERROR);
}
/* Read message type */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* Read message sub type */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* Read rcvd_pad bits */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive transaction id */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive timeout value */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive message length */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive status field */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive message checksum */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* receive header checksum */
PCPL_IO_OP_READ)) != 0) {
return (ret);
}
/* copy to resp_hdr */
return (PCP_OK);
}
/*
* Get next xid for including in request message.
* Every request and response message are matched
* for same xid.
*/
static uint32_t
pcp_get_xid(void)
{
if (xid_initialized == B_FALSE) {
/*
* starting xid is initialized to a different value everytime
* user application is restarted so that user apps will not
* receive previous session's packets.
*
* Note: The algorithm for generating initial xid is partially
* taken from Solaris rpc code.
*/
}
/* zero xid is not allowed */
if (ret == 0)
return (ret);
}
/*
* This function handles channel framing errors. It waits until proper
* frame with starting sequence as magic numder (0xAFBCAFA0)
* is arrived. It removes unexpected data (before the magic number sequence)
* on the channel. It returns when proper magic number sequence is seen
*/
static int
pcp_frame_error_handle(void)
{
int ispresent = 0;
while (!ispresent) {
/*
* Check if next four bytes matches pcp magic number.
* if mathing not found, discard 1 byte and continue checking.
*/
&ispresent)) {
if (!ispresent) {
/* remove 1 byte */
}
} else {
return (-1);
}
}
return (0);
}
/*
* checks whether certain byte sequence is present in the data stream.
*/
static int
{
int ret, i;
return (ret);
}
/* 'byte_cnt' bytes not present */
*ispresent = 0;
return (0);
}
for (i = 0; i < byte_cnt; ++i) {
*ispresent = 0;
return (0);
}
}
*ispresent = 1;
return (0);
}
/*
* 16-bit simple internet checksum
*/
static uint16_t
{
/*
* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
while (count > 1) {
/* This is the inner loop */
count -= 2;
}
/* Add left-over byte, if any */
if (count > 0)
/* Fold 32-bit sum to 16 bits */
while (sum >> 16)
if (sum == 0)
sum = 0xffff;
return (sum);
}
/*
* cleanup the channel if any data is hanging in
* channel buffers.
*/
static int
pcp_cleanup(int channel_fd)
{
int ret;
int n, done;
int retry = 0;
return (PCPL_MALLOC_FAIL);
}
/*
* set sig jmp location
*/
return (PCPL_GLVC_TIMEOUT);
}
done = 0;
while (!done) {
(void) alarm(PCP_CLEANUP_TIMEOUT);
&peek_ctrl)) < 0) {
(void) alarm(0);
done = 1;
continue;
}
(void) alarm(0);
if (n <= 0 && retry > 2) {
done = 1;
continue;
} else if (n <= 0) {
++retry;
continue;
}
/* remove data from channel */
(void) alarm(PCP_CLEANUP_TIMEOUT);
(void) alarm(0);
done = 1;
continue;
}
(void) alarm(0);
}
return (ret);
}