/** @file ipc_call.c @date 2008-06-30 @author 박진호 jhpark@falinux.com @brief 프로세스간의 명령 전달을 구현한다. @modify @todo @bug @remark @warning */ // // 저작권 에프에이리눅스(주) // 외부공개 금지 // //---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// @brief 전역 변수 선언 //------------------------------------------------------------------------------ /// @} char ipc_call_desc[] = "ipc_call ver 0.3.0"; #define FABIND_QUEUE_COUNT 1024 /// fabind 개별 구조체 typedef struct { unsigned long id; /// unsigned long port; /// 통신을 위한 PORT unsigned long group; /// 구룹 처리를 위한 GROUP ID long method_type; /// 통신 처리 방법 ( FABIND, UDS, UDP ) unsigned long level; /// LEVEL 값 unsigned long time_out; /// TIME OUT 값 poll_obj_t *poll_obj; /// 폴 관련 obj ipc_call_func_t *func; /// 데이타 수신시 호출하는 사용자 콜백함수 ipc_call_packet_t recv_packet; /// 데이타 수신 패켓 } ipc_priv_t; /// IPC_CALL 의 변수를 관리하는 구조체 static ipc_priv_t ipc_priv; int ipc_call_poll_in( poll_obj_t *obj); int ipc_call_method ( unsigned long method ); //------------------------------------------------------------------------------ /** @brief IPC_CALL 에러 상태를 출력 한다. @param err err 넘버 *///---------------------------------------------------------------------------- void ipc_call_print_error ( int err ) { if ( 0 > err ) { switch( err ) { case IPC_CALL_E_AGAIN : printf("ipc_call) Error Again\n"); break; case IPC_CALL_E_TIME_OUT : printf("ipc_call) Error Timeout\n"); break; case IPC_CALL_E_BUSY : printf("ipc_call) Error Busy\n"); break; case IPC_CALL_E_NO_ID : printf("ipc_call) Error No id\n"); break; } } } //------------------------------------------------------------------------------ /** @brief IPC_CALL 을 생성한다. @param id IPC의 ID. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_create ( unsigned long id ) { poll_obj_t *obj; memset( (void *)&ipc_priv, 0, sizeof(ipc_priv_t) ); ipc_priv.id = id; ipc_priv.port = IPC_CALL_GET_PORT(id) ; ipc_priv.group = IPC_CALL_GET_GROUP(id); ipc_priv.method_type = IPC_CALL_METHOD_NONE; // 디폴트로 UDS로 열고 다른것은 타입을 변경할때 처리 하도록 한다. return ipc_call_method( IPC_CALL_METHOD_UDS ); } //------------------------------------------------------------------------------ /** @brief IPC_CALL 을 해제한다. @param id IPC의 ID. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_free( unsigned long id ) { poll_obj_t *obj; if ( ipc_priv.id != id ) return IPC_CALL_E_NO_ID; obj = ipc_priv.poll_obj; switch( ipc_priv.method_type ) { case IPC_CALL_METHOD_NONE : break; case IPC_CALL_METHOD_FABIND : fabind_close( obj ); break; case IPC_CALL_METHOD_UDS : uds_close( obj ); break; case IPC_CALL_METHOD_UDP : udp_close( obj ); break; default : printf("Not Match Method\n"); break; } return IPC_CALL_OK; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 수행 방식을 정한다. @param method 메시지 전송 방법 @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_method ( unsigned long method ) { poll_obj_t *obj = NULL; if ( ipc_priv.method_type == method ) return IPC_CALL_OK; ipc_call_free( ipc_priv.id ); switch( method ) { case IPC_CALL_METHOD_FABIND : { obj = fabind_open( ipc_priv.port, ipc_priv.group, FABIND_QUEUE_COUNT); if ( NULL == obj ) { return IPC_CALL_E_BUSY; } ipc_priv.method_type = IPC_CALL_METHOD_FABIND; } break; case IPC_CALL_METHOD_UDS : { char dst_uds[1024]; sprintf( dst_uds, IPC_CALL_UDS_NAME_FORMAT, ipc_priv.port ); obj = uds_open_server( dst_uds ); if ( NULL == obj ) { return IPC_CALL_E_BUSY; } ipc_priv.method_type = IPC_CALL_METHOD_UDS; } break; case IPC_CALL_METHOD_UDP : { obj = udp_open_server( ipc_priv.port ); if ( NULL == obj ) { return IPC_CALL_E_BUSY; } ipc_priv.method_type = IPC_CALL_METHOD_UDP; } break; default : printf("Not Match METHOD\n"); break; } if( NULL != obj ) { obj->on_poll_in = ipc_call_poll_in; ipc_priv.poll_obj = obj; } return IPC_CALL_OK; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 레벨을 정한다. @param level 전송 레벨을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_level ( unsigned long level ) { ipc_priv.level = level; return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 디폴트 레벨을 설정한다. @param level 디폴트 전송 레벨을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_level_default ( unsigned long level ) { ipc_priv.level = level; return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 시간 초과를 설정한다. @param msec timeout될 시간을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_timeout ( unsigned long msec ) { ipc_priv.time_out = msec; return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 디폴트 시간 초과를 정한다. @param msec timeout될 디폴트 시간을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_timeout_default ( unsigned long msec ) { ipc_priv.time_out = msec; return 0; } //------------------------------------------------------------------------------ /** @brief POLLIN을 위한 함수 포인터 @param obj poll_obj @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_poll_in( poll_obj_t *obj) { ipc_call_packet_t *packet; int ack, rsize = 0; packet = &(ipc_priv.recv_packet); switch( ipc_priv.method_type ) { case IPC_CALL_METHOD_FABIND : rsize = fabind_read( obj, (void *)packet, sizeof(ipc_call_packet_t), &ack ); break; case IPC_CALL_METHOD_UDS : rsize = uds_read( obj, (void *)packet, sizeof(ipc_call_packet_t) ); break; case IPC_CALL_METHOD_UDP : rsize = udp_read( obj, (void *)packet, sizeof(ipc_call_packet_t) ); break; default : printf("Not Match Method\n"); break; } #if 0 { int i; printf("rsize:%d ======================\n", rsize); for( i = 0; i < packet->size; i++) { if ( i%10 == 0 ) printf("\n"); printf("%X ", packet->buf[i]); } printf("\n=============================\n"); } #endif if ( ( 0 < rsize ) && ipc_priv.func ) { ipc_call_func_t read_func; read_func = ipc_priv.func; read_func( packet->src_id, packet->msg_type, packet->buf, packet->size ); } return POLL_EVENTED; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 읽기에 대한 이벤트를 지정한다. @param func read시 호출될 함수 포인터를 설정 한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_on_read ( ipc_call_func_t *func ) { ipc_priv.func = func; return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 수신된 버퍼의 내용을 BIN 형식으로 읽는다. @param msec timeout될 디폴트 시간을 정한다. @return 0 성공 -1 실패 @remark 이 함수는 필요가 없을것 같다. 판단필요 *///---------------------------------------------------------------------------- int ipc_call_read ( void *buf, int len ) { poll_obj_t *obj; int rsize = 0; int ack; char rbuf[1024]; memset( rbuf, 0x00, sizeof(rbuf) ); obj = ipc_priv.poll_obj; switch( ipc_priv.method_type ) { case IPC_CALL_METHOD_FABIND : rsize = fabind_read( obj, rbuf, len, &ack ); break; case IPC_CALL_METHOD_UDS : rsize = uds_read( obj, rbuf, len ); break; case IPC_CALL_METHOD_UDP : rsize = udp_read( obj, rbuf, len ); break; default : printf("Not Match METHOD\n"); break; } memcpy( buf, rbuf, rsize ); return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 BIN 형식으로 데이터를 전송한다. @param msec timeout될 디폴트 시간을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_send ( unsigned long dest, void *buf, int len, unsigned long msg_type ) { poll_obj_t *obj; ipc_call_packet_t packet; int ret = 0; // printf( "IPC_CALL_SEND DATA[%s] LEN=%d\n", buf, len ); if ( 0 >= len ) return IPC_CALL_E_EMPTY; if ( len > IPC_CALL_BUF_LEN ) len = IPC_CALL_BUF_LEN; obj = ipc_priv.poll_obj; packet.dst_id = dest; packet.src_id = ipc_priv.id; packet.msg_type = msg_type; packet.size = len; memcpy( packet.buf, buf, len); switch( ipc_priv.method_type ) { case IPC_CALL_METHOD_FABIND : { ret = fabind_write( obj, packet.dst_id, (char *)(&packet), IPC_CALL_PACKET_HEADER_LEN + len ); } break; case IPC_CALL_METHOD_UDS : { char dst_uds[1024]; //memset(dst_uds, 0x00, sizeof(dst_uds)); //sprintf(dst_uds, "%s%ld", IPC_UDS_NAME_PREFIX, dest ); sprintf( dst_uds, IPC_CALL_UDS_NAME_FORMAT, IPC_CALL_GET_PORT(dest) ); ret = uds_write( obj, dst_uds, (char *)(&packet), IPC_CALL_PACKET_HEADER_LEN + len ); } break; case IPC_CALL_METHOD_UDP : { char host[1024]; //memset(host, 0x00, sizeof(host) ); sprintf(host, "%s", "127.0.0.1" ); ret = udp_write( obj, host, IPC_CALL_GET_PORT(dest), (char *)(&packet), IPC_CALL_PACKET_HEADER_LEN + len ); } break; default : { printf("Not Match Method\n"); } break; } if ( 0 >= ret ) { // todo printf("ipc Send Fail\n"); return -1; } return 0; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 XML 형식으로 데이터를 전송한다. @param msec timeout될 디폴트 시간을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_send_xml ( unsigned long dest, char *buf, int len ) { return ipc_call_send( dest, buf, len, IPC_CALL_MSG_TYPE_XML ); } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 ASC 형식으로 데이터를 전송한다. @param msec timeout될 디폴트 시간을 정한다. @return 0 성공 -1 실패 *///---------------------------------------------------------------------------- int ipc_call_send_asc ( unsigned long dest, char *buf, int len ) { return ipc_call_send( dest, buf, len, IPC_CALL_MSG_TYPE_ASC ); } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 BIN 형식으로 데이터를 전송하고 수신한다. @param msec timeout될 디폴트 시간을 정한다. @return 양수 성공 음수 실패 *///---------------------------------------------------------------------------- int ipc_call_rpc_raw ( unsigned long dest, void *tbuf, int tlen , void *rbuf, int rlen , unsigned long msg_type ) { int ret = 0; int event_ret = 0; ipc_call_packet_t packet; poll_obj_t *obj; packet.dst_id = dest; packet.src_id = ipc_priv.id; packet.msg_type = msg_type; packet.size = tlen; memcpy( packet.buf, tbuf, tlen); obj = ipc_priv.poll_obj; switch( ipc_priv.method_type ) { case IPC_CALL_METHOD_FABIND : { ret = fabind_write_and_response( obj, packet.dst_id, tbuf, rbuf, tlen, rlen ); } break; case IPC_CALL_METHOD_UDS : { char dst_uds[1024]; sprintf( dst_uds, IPC_CALL_UDS_NAME_FORMAT, IPC_CALL_GET_PORT(dest) ); ret = uds_write( obj, dst_uds, (char *)(&packet), IPC_CALL_PACKET_HEADER_LEN + tlen ); event_ret = poll_do_one( obj->fd, POLLIN, ipc_priv.time_out ); // POLL 이벤트가 올때까지 대기 한다. ( TIME OUT )이면 빠져 나간다. if ( event_ret != POLL_EVENTED ) { // 이벤트가 정상적으로 발생 할때 까지 무한 대기 한다. while(1) { ret = uds_write( obj, dst_uds, (char *)(&packet), IPC_CALL_PACKET_HEADER_LEN + tlen ); if ( ( event_ret == POLL_TIME_OUT ) && ( ipc_priv.time_out == IPC_CALL_TIMEOUT_UNLIMIT ) ) { event_ret = poll_do_one( obj->fd, POLLIN, ipc_priv.time_out ); // POLL 이벤트가 올때까지 대기 한다. ( TIME OUT )이면 빠져 나간다. } if ( event_ret == POLL_EVENTED ) { break; } } } ret = uds_read( obj, rbuf, rlen ); } break; case IPC_CALL_METHOD_UDP : { char host[1024]; memset(host, 0x00, sizeof(host) ); sprintf(host, "%s", "127.0.0.1"); udp_write( obj, host, packet.dst_id, (char *)(&packet), tlen ); poll_do_one( obj->fd, POLLIN, ipc_priv.time_out ); // POLL 이벤트가 올때까지 대기 한다. ( TIME OUT )이면 빠져 나간다. ret = udp_read( obj, rbuf, rlen ); } break; default : { printf("Not Match METHOD\n"); } break; } if ( ret < 0 ) ipc_call_print_error( ret ); return ret; } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 BIN 형식으로 데이터를 전송하고 수신한다. @param msec timeout될 디폴트 시간을 정한다. @return 양수 성공 음수 실패 *///---------------------------------------------------------------------------- int ipc_call_rpc( unsigned long dest, void *tbuf, int tlen , void *rbuf, int rlen ) { return ipc_call_rpc_raw( dest, tbuf, tlen , rbuf, rlen , IPC_CALL_MSG_TYPE_BIN ); } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 XML 형식으로 데이터를 전송하고 수신한다. @param msec timeout될 디폴트 시간을 정한다. @return 양수 성공 음수 실패 *///---------------------------------------------------------------------------- int ipc_call_rpc_xml( unsigned long dest, void *tbuf, int tlen , void *rbuf, int rlen ) { return ipc_call_rpc_raw( dest, tbuf, tlen , rbuf, rlen , IPC_CALL_MSG_TYPE_XML ); } //------------------------------------------------------------------------------ /** @brief IPC_CALL 의 ASC 형식으로 데이터를 전송하고 수신한다. @param msec timeout될 디폴트 시간을 정한다. @return 양수 성공 음수 실패 *///---------------------------------------------------------------------------- int ipc_call_rpc_asc( unsigned long dest, void *tbuf, int tlen , void *rbuf, int rlen ) { return ipc_call_rpc_raw( dest, tbuf, tlen , rbuf, rlen , IPC_CALL_MSG_TYPE_ASC ); }