/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) illp-session.cc (Gunther Schadow) 07/01/96"); #pragma implementation "illp-session.h" #include "illp-session.h" #include "exception.h" #include "logfile.h" #include #include #include #include #include #include #include #include #ifdef sun extern "C" int wait3(int*, int, struct rusage*); extern "C" int recv(int, char*, int, int); extern "C" int ioctl(int, int, void*); #endif #include #include #include #include #include #include "ParseTrace.h" #include "hl7/parsetrace.h" #include "ANYmsg.h" #include "ACKmsg.h" bool illp_do_not_fork = false; #define A_STX 0x02 #define A_CAN 0x18 static bool message_restarted = FALSE; static bool message_cancelled = FALSE; static iosockinet *message_io = NULL; static ANYmsg *error_anymsg = NULL; static const char *theApp = NULL; static const char *theFac = NULL; static void error_handler(int typ, const char *msg); static int oob_handler(Signal&); static int recv_oob(sockinetbuf &isb); #ifdef HL7V21 #define CAval AAval #endif iosockinet *connect_illp(const char *host, const char *service, connect_id_t connect_id) { LOGDEBUG("trying host: %s, service: %s", host, service); iosockinet *server = new iosockinet(sockbuf::sock_stream); (*server)->connect(host,service,"tcp"); LOGDEBUG("connected to host: %s, port: %d", (*server)->peerhost(), (*server)->peerport()); if(connect_id == NULL) *server << (*server)->localhost() << (char)A_STX; else *server << connect_id << (char)A_STX; server->flush(); new xios(*server); hl7er(*server); return server; } void timeout(Timer &) { ERROR("timeout processing message"); } iosockinet *accept_illp(const char *service, connect_id_t *connect_id = NULL) { sockinetbuf serve_sb(sockbuf::sock_stream); serve_sb.bind(INADDR_ANY,service,"tcp"); LOGDEBUG("bind port: %d", serve_sb.localport()); serve_sb.listen(5); iosockinet *client_io; #ifndef DEBUG while(1) { Proc child; #endif LOGNOTICE("accepting connections at port: %d", serve_sb.localport()); client_io = new iosockinet(serve_sb.accept()); #ifndef DEBUG if(!illp_do_not_fork) child.run(); if(! child.parent()) // child or not spawned at all { #endif /* timeout timer two minutes */ Timer timer(2 * 60,0,0,0,timeout); serve_sb.close(); char c; connect_id_t peer_peer; client_io->scan(" %127[^\02] %c", peer_peer, &c); LOGNOTICE("connection made: %s via %s", peer_peer, (*client_io)->peerhost()); if(connect_id != NULL) strcpy(*connect_id, peer_peer); new xios(*client_io); hl7er(*client_io); return client_io; #ifndef DEBUG } else // parent (*client_io)->close(); } #endif } void close_illp(iosockinet *io) { if(error_anymsg != NULL) { error_anymsg = NULL; exception_handler(E_PARSE, NULL); exception_handler(E_FATAL, NULL); exception_handler(E_ERROS, NULL); exception_handler(E_ERROR, NULL); } parseTrace.record_function = null_record; parseErrorSegment.unset(); delete __xios(*io); (*io)->close(); } result receive_message(class iosockinet *io, class ANYmsg &any, const char *App, const char *Fac) { iosockinet &client_io = *io; /* * Prepare for local exceptions */ message_io = &client_io; error_anymsg = &any; theApp = App; theFac = Fac; exception_handler(E_PARSE, error_handler); exception_handler(E_FATAL, error_handler); exception_handler(E_ERROS, error_handler); exception_handler(E_ERROR, error_handler); parseTrace.record_function = hl7_record; /* * Prepare for remote exceptions */ lprintf(L_NOISE, "file descriptor: %d", client_io->operator int()); #ifdef hpux pid_t my_pid = getpid(); if(ioctl(client_io->operator int(), SIOCSPGRP, &my_pid) == FAIL) { lprintf(L_ERROS, "SIOCSPGRP"); #else if(fcntl(client_io->operator int(), F_SETOWN, getpid()) == FAIL) { lprintf(L_ERROS, "F_SETOWN"); #endif exit(1); } Signal sigurg(SIGURG, oob_handler); /* * Read Message */ result recv_res; do { message_restarted = FALSE; message_cancelled = FALSE; recv_res = any.input(client_io); if(message_restarted) client_io.clear(); } while(message_restarted); sigurg.ignore(); if(recv_res != SUCCESS) { LOGWARNING("failed"); client_io.clear(); return FAIL; } else { LOGDEBUG("success"); client_io.clear(); return SUCCESS; } } result send_message(class iosockinet *io, class HL7Message &msg) { iosockinet &client_io = *io; msg.output(client_io); client_io.flush(); client_io->shutdown(sockbuf::shut_write); return SUCCESS; } result chat_illp(const char *host, const char *serv, class HL7Message &request, class ANYmsg &result, connect_id_t cid) { iosockinet *io = connect_illp(host, serv, cid); if(io == NULL) return FAIL; else { if(send_message(io, request) == FAIL) goto fail; if(receive_message(io, result) == FAIL) goto fail; close_illp(io); if(result.type() == UniMesIdCode::ACKval) goto check_ack; else return SUCCESS; fail: close_illp(io); return FAIL; } check_ack: ACKmsg &ack = (ACKmsg &)(*result); if(ack.MesAck.MesConTrolId != request.MesHea.MesConTrolId) { LOGWARNING("message control ids do not match: `%s' `%s'", (const char *)ack.MesAck.MesConTrolId, (const char *)request.MesHea.MesConTrolId); return FAIL; } else if(ack.MesAck.Ack != AckCode::AAval && ack.MesAck.Ack != AckCode::CAval) { LOGWARNING("message error: `%s'", (const char *)ack.MesAck.Ack); for(repfield *rerr = &ack.Error.ErrorCodeAndLoc; !rerr->end(); rerr = rerr->next()) { ZEtyp &err = rerr->first(); strstream s; xios _xsb(s); s << hl7er << err << '\0'; LOGWARNING(" %s", s.str()); s.freeze(0); } return FAIL; } else return SUCCESS; } static int oob_handler(Signal &) { iosockinet &client_io = *message_io; LOGWARNING("SIGURG trapped"); char c = recv_oob(*client_io.rdbuf()); switch(c) { case A_STX: lprintf(L_WARN, "message restarted"); message_restarted = TRUE; break; case A_CAN: lprintf(L_WARN, "transaction cancelled by server"); message_cancelled = TRUE; break; case FAIL: lprintf(L_ERROS, "receive oob failed"); message_cancelled = TRUE; break; default: lprintf(L_ERROR, "undefined oob signal: 0x%02x", (int)c); message_cancelled = TRUE; break; } client_io.setstate(ios::eofbit); return 0; } static int recv_oob(sockinetbuf &isb) { int atmark = FALSE; char waist[1024]; char c = 0; do { if(ioctl((int)isb, SIOCATMARK, &atmark) == FAIL) lprintf(L_ERROS, "SIOCATMARK"); if(atmark) break; if(read((int)isb, waist, 1024) == 0) { lprintf(L_ERROR, "connection closed by client"); return FAIL; } } while(TRUE); int n = recv((int)isb, &c, 1, MSG_OOB); if(n < 1) lprintf(L_ERROS, "recv MSG_OOB"); if(n == 0) { lprintf(L_ERROR, "connection closed by client"); return FAIL; } return c; } static void error_handler(int typ, const char *msg) { iosockinet &io = *message_io; ANYmsg &any = *error_anymsg; char waist[1024]; /* * read to end of message */ while(io->read(waist, 1024) > 0); io.clear(); /* * Assemble ACK message */ ACKmsg ack; // Return address and reference (if any) if(any.ispresent() && any->MesHea.ispresent()) { ack.MesHea.RecApp = any->MesHea.SenApp; ack.MesHea.RecFac = any->MesHea.SenFac; ack.MesHea.MesConTrolId = any->MesHea.MesConTrolId; } else { ack.MesHea.RecApp.nullify(); ack.MesHea.RecFac.nullify(); ack.MesHea.MesConTrolId.nullify(); } // MSA and ERR segment if(typ == E_PARSE) { ack.MesAck.Ack = AckCode::AppRej; ack.MesAck.TextMes = "parse error"; ack.Error = parseErrorSegment; } else { ack.MesAck.Ack = AckCode::AppRej; ack.MesAck.TextMes = msg; ack.Error = parseErrorSegment; } // MSH segment ack.MesHea.SenApp = theApp; ack.MesHea.SenFac = theFac; stamp(ack.MesHea.DateTimeOfMes); char xanbuf[20]; sprintf(xanbuf,"RES%05u", (u_int)getpid()); ack.MesHea.MesConTrolId = xanbuf; ack.MesHea.ProId = ProIdCode::Pro; #ifdef HL7V21 ack.MesHea.VerId = 2.1; #else ack.MesHea.VerId = VerIdCode::_2_2val; #endif ack.commit(); ack.output(io); io.flush(); io->shutdown(sockbuf::shut_write); LOGWARNING("error exit"); exit(1); }