/*************************************************************************** $RCSfile: kvkd.cpp,v $ ------------------- cvs : $Id: kvkd.cpp,v 1.13 2003/05/08 11:01:23 aquamaniac Exp $ begin : Thu Dec 12 2002 copyright : (C) 2002 by Martin Preuss email : martin@libchipcard.de **************************************************************************** * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif /* Internationalization */ #ifdef HAVE_GETTEXT_ENVIRONMENT # include # include # define I18N(m) gettext(m) #else # define I18N(m) m #endif #define I18NT(m) m #include #include #include #include #include #include #include #include using namespace std; #ifdef HAVE_SIGNAL_H # include #endif #define RV_OK 0 #define RV_HARDWARE 1 #define RV_DISC 2 #define RV_NOKVK 3 static int KVKDaemonStop=0; #define k_PRG "kvkd" #define k_PRG_VERSION_INFO \ "kvkd v0.2 (part of libchipcard v"k_CHIPCARD_VERSION_STRING")\n"\ "(c) 2003 Martin Preuss\n" \ "This program is free software licensed under GPL.\n"\ "See COPYING for details.\n" void usage(string name) { fprintf(stderr,"%s%s%s%s%s%s", I18N("KVK Daemon - A daemon for German medical cards.\n" "(c) 2003 Martin Preuss\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n" "License as published by the Free Software Foundation; either\n" "version 2.1 of the License, or (at your option) any later version.\n" "\n" "Usage:\n" "%s [OPTIONS]\n" " OPTIONS\n" " -C CONFIGFILE - use the given LibChipCard configuration file\n" " (defaults to "CHIPCARDC_CFGFILE"\n" " -d DESTDIR - destination folder for the card data read\n"), #ifdef HAVE_FORK I18N(" -f - start in foreground mode (otherwise fork into\n" " background)\n"), #else "", #endif I18N(" --logfile F - use given F as log file\n" " --logtype T - use given T as log type\n" " These are the valid types:\n" " stderr (log to standard error channel)\n" " file (log to the file given by --logfile)\n"), #ifdef HAVE_SYSLOG_H I18N(" syslog (log via syslog)\n" " Default is syslog\n"), #else I18N(" Default is stderr\n"), #endif I18N(" --loglevel L - set the loglevel\n" " Valid levels are:\n" " emergency, alert, critical, error,\n" " warning, notice, info and debug\n" " Default is \"notice\".\n" " --nobeep - disable beep after reading a card (once if ok,\n" " twice or even more often on error)\n" " --beeps X - number of beeps for severe errors, such as:\n" " - card is not recognized as a medical card\n" " - no space on disc to store the data\n" " X defaults to 4\n") , name.c_str()); } struct s_args { string configFile; // -C string destDir; // -d string logFile; // --logfile LOGGER_LOGTYPE logType; // --logtype LOGGER_LEVEL logLevel; // --loglevel #ifdef HAVE_FORK bool daemonMode; // -f #endif bool beep; // --nobeep int errorbeeps; // --beeps list params; }; int checkArgs(s_args &args, int argc, char **argv) { int i; string tmp; i=1; args.configFile=CHIPCARDC_CFGFILE; args.destDir="/var/spool/gnumed"; #ifdef HAVE_FORK args.daemonMode=true; #endif args.logFile="kvkd.log"; #ifdef HAVE_SYSLOG_H args.logType=LoggerTypeSyslog; #else args.logType=LoggerTypeConsole; #endif args.logLevel=LoggerLevelNotice; args.beep=true; args.errorbeeps=6; while (i=argc) return 1; args.destDir=argv[i]; } else if (tmp=="-C") { i++; if (i>=argc) return 1; args.configFile=argv[i]; } #ifdef HAVE_FORK else if (tmp=="-f") { args.daemonMode=false; } #endif else if (tmp=="--nobeep") { args.beep=false; } else if (tmp=="--beeps") { i++; if (i>=argc) return 1; args.errorbeeps=atoi(argv[i]); } else if (tmp=="-h" || tmp=="--help") { usage(argv[0]); return -1; } else if (tmp=="-V" || tmp=="--version") { fprintf(stdout,k_PRG_VERSION_INFO); return -1; } else if (tmp=="--logfile") { i++; if (i>=argc) return 1; args.logFile=argv[i]; } else if (tmp=="--logtype") { i++; if (i>=argc) return 1; if (strcmp(argv[i],"stderr")==0) args.logType=LoggerTypeConsole; else if (strcmp(argv[i],"file")==0) args.logType=LoggerTypeFile; #ifdef HAVE_SYSLOG_H else if (strcmp(argv[i],"syslog")==0) args.logType=LoggerTypeSyslog; #endif else { fprintf(stderr,I18N("Unknown log type \"%s\"\n"), argv[i]); return 1; } } else if (tmp=="--loglevel") { i++; if (i>=argc) return 1; if (strcmp(argv[i], "emergency")==0) args.logLevel=LoggerLevelEmergency; else if (strcmp(argv[i], "alert")==0) args.logLevel=LoggerLevelAlert; else if (strcmp(argv[i], "critical")==0) args.logLevel=LoggerLevelCritical; else if (strcmp(argv[i], "error")==0) args.logLevel=LoggerLevelError; else if (strcmp(argv[i], "warning")==0) args.logLevel=LoggerLevelWarning; else if (strcmp(argv[i], "notice")==0) args.logLevel=LoggerLevelNotice; else if (strcmp(argv[i], "info")==0) args.logLevel=LoggerLevelInfo; else if (strcmp(argv[i], "debug")==0) args.logLevel=LoggerLevelDebug; else { fprintf(stderr, I18N("Unknown log level \"%s\"\n"), argv[i]); return 1; } } else // otherwise add param args.params.push_back(tmp); i++; } // while // that's it return 0; } #ifdef HAVE_SIGACTION /* Signal handler */ void signalHandler(int s) { switch(s) { case SIGINT: DBG_NOTICE("Got an interrupt signal\n"); KVKDaemonStop=1; break; case SIGTERM: DBG_NOTICE("Got an termination signal\n"); KVKDaemonStop=1; break; #ifdef SIGINFO case SIGINFO: DBG_NOTICE("Got an info signal\n"); break; #endif #ifdef SIGHUP case SIGHUP: DBG_NOTICE("Got a hangup signal\n"); break; #endif default: DBG_ERROR("Unknown signal %d",s); break; } /* switch */ } struct sigaction saINT, saTERM, saINFO, saHUP; int setSignalHandler() { saINT.sa_handler=signalHandler; sigemptyset(&saINT.sa_mask); saINT.sa_flags=0; if (sigaction(SIGINT, &saINT,0)) { fprintf(stderr,I18N("Could not setup signal handler for SIGINT\n")); return 2; } saTERM.sa_handler=signalHandler; sigemptyset(&saTERM.sa_mask); saTERM.sa_flags=0; if (sigaction(SIGTERM, &saTERM,0)) { fprintf(stderr,I18N("Could not setup signal handler for SIGTERM\n")); return 2; } #ifdef SIGINFO saINFO.sa_handler=signalHandler; sigemptyset(&saINFO.sa_mask); saINFO.sa_flags=0; if (sigaction(SIGINT, &saINFO,0)) { fprintf(stderr,I18N("Could not setup signal handler for SIGINFO\n")); return 2; } #endif #ifdef SIGHUP saHUP.sa_handler=signalHandler; sigemptyset(&saHUP.sa_mask); saINT.sa_flags=0; if (sigaction(SIGHUP, &saHUP,0)) { fprintf(stderr,I18N("Could not setup signal handler for SIGHUP\n")); return 2; } #endif return 0; } #endif int handleCard(s_args &args, CTPointer basecard){ CTError err, err2; insuranceData idata; string filenamebase; string filename; string tmpname; FILE *f; int i; struct tm *ltime; time_t tms; CTPointer card; DBG_NOTICE("Got a card"); card=new CTKVKCard(basecard.ref()); basecard=0; err=card.ref().openCard(); if (!err.isOk()) { DBG_ERROR("Error in openCard: %s", err.errorString().c_str()); return RV_NOKVK; } err=card.ref().readCardData(idata); err2=card.ref().closeCard(); if (!err.isOk()) { DBG_ERROR("Error in readCardData: %s\n", err.errorString().c_str()); if (err.code()==k_CTERROR_INVALID) { return RV_NOKVK; } return RV_HARDWARE; } if (!err2.isOk()) { DBG_ERROR("Error in closeCard: %s", err.errorString().c_str()); return RV_HARDWARE; // HW-ERROR } if (!args.daemonMode) DBG_INFO("Card reading done."); filenamebase=args.destDir; if (filenamebase.empty()) { DBG_ERROR("Missing destination directory !"); return RV_DISC; } if (filenamebase[filenamebase.length()-1]!='/') filenamebase+="/"; // get current date and time tms=time(NULL); ltime=localtime(&tms); // try to create a unique filename i=10; while(i) { filename=filenamebase; filename+="KVK-"; #ifdef HAVE_RANDOM filename+=CTMisc::num2string(random()); #else filename+=CTMisc::num2string(rand()); #endif filename+=".dat"; // check whether the file exists f=fopen(filename.c_str(),"r"); if (f!=0) fclose(f); if (f==0) // found a free name break; i--; } if (!i) { DBG_ERROR("WARNING: Could not create a unique name within 10 tries !\n" "Please report to bugs@libchipcard.de"); return RV_DISC; } tmpname=filename+".tmp"; f=fopen(tmpname.c_str(),"w+"); if (f==0) { DBG_ERROR("Could not create file \"%s\", reason: \n %s", tmpname.c_str(),strerror(errno)); return RV_DISC; } // now print the data fprintf(f,"Version:libchipcard-"k_CHIPCARD_VERSION_STRING"\n"); fprintf(f,"Datum:%02d.%02d.%04d\n", ltime->tm_mday, ltime->tm_mon, ltime->tm_year+1900); fprintf(f,"Zeit:%02d:%02d:%02d\n", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); fprintf(f,"KK-Name:%s\n",idata.insuranceCompanyName.c_str()); fprintf(f,"KK-Nummer:%s\n",idata.insuranceCompanyCode.c_str()); fprintf(f,"KVK-Nummer:%s\n",idata.cardNumber.c_str()); fprintf(f,"V-Nummer:%s\n",idata.insuranceNumber.c_str()); fprintf(f,"V-Status:%s\n",idata.insuranceState.c_str()); fprintf(f,"V-Statusergaenzung:%s\n",idata.eastOrWest.c_str()); fprintf(f,"Titel:%s\n",idata.title.c_str()); fprintf(f,"Vorname:%s\n",idata.forename.c_str()); fprintf(f,"Namenszusatz:%s\n",idata.nameSuffix.c_str()); fprintf(f,"Familienname:%s\n",idata.name.c_str()); fprintf(f,"Geburtsdatum:%s\n",idata.dateOfBirth.c_str()); fprintf(f,"Strasse:%s\n",idata.addrStreet.c_str()); fprintf(f,"Laendercode:%s\n",idata.addrState.c_str()); fprintf(f,"PLZ:%s\n",idata.addrPostalCode.c_str()); fprintf(f,"Ort:%s\n",idata.addrCity.c_str()); fprintf(f,"gueltig-bis:%s\n",idata.bestBefore.c_str()); fprintf(f,"Pruefsumme-gueltig:%s\n", idata.isValid?"ja":"nein"); fprintf(f,"Kommentar:derzeit keiner\n"); if (fclose(f)) { DBG_ERROR("Could not close file \"%s\", reason: \n %s", tmpname.c_str(),strerror(errno)); return RV_DISC; } // finally move the file if (rename(tmpname.c_str(),filename.c_str())) { DBG_ERROR("Could not rename file \"%s\" to \"%s\", reason: \n %s", tmpname.c_str(),filename.c_str(),strerror(errno)); return RV_DISC; } if (!args.daemonMode) DBG_NOTICE("Card done."); return RV_OK; } int main(int argc, char **argv) { int rv; CTPointer card; CTPointer trader; s_args args; string cmd; int i; CTError err; #ifdef HAVE_GETTEXT_ENVIRONMENT setlocale(LC_ALL,""); if (bindtextdomain("kvkd", I18N_PATH)==0) { fprintf(stderr," Error bindtextdomain()\n"); } if (textdomain("kvkd")==0) { fprintf(stderr," Error textdomain()\n"); } #endif rv=checkArgs(args,argc,argv); if (rv==-1) return 0; else if (rv) return rv; #ifdef HAVE_FORK if (args.daemonMode) { rv=fork(); if (rv==-1) { fprintf(stderr,I18N("Error on fork, aborting.\n")); return 2; } else if (rv!=0) { fprintf(stderr,I18N("Daemon forked, father exiting.\n")); return 0; } } #endif #ifdef HAVE_SIGACTION if (setSignalHandler()) { fprintf(stderr,I18N("Error setting up signal handler, aborting\n")); return 2; } #endif /* setup random number generator * Note: The check for HAVE_RANDOM is ok, because I think it's better to * use matching function calls (random-srandom / rand/srand) */ #ifdef HAVE_RANDOM srandom(time(NULL)); #else srand(time(NULL)); #endif /* setup logging */ if (Logger_Open("kvkd", args.logFile.c_str(), args.logType, LoggerFacilityUser)) { fprintf(stderr,I18N("Could not start logging, aborting.\n")); return 2; } Logger_SetLevel(args.logLevel); rv=ChipCard_Init(args.configFile.c_str(),0); if (rv!=CHIPCARD_SUCCESS) { fprintf(stderr,I18N("Could not init libchipcard (%d)\n"),rv); return 2; } trader=new CTCardTrader(false, 0, 0, CHIPCARD_STATUS_INSERTED, CHIPCARD_STATUS_INSERTED | CHIPCARD_STATUS_LOCKED_BY_OTHER, CHIPCARD_STATUS_INSERTED); err=trader.ref().start(); if (!err.isOk()) { fprintf(stderr,I18N("Could not init card trader (%s)\n"), err.errorString().c_str()); return 2; } while(KVKDaemonStop==0) { CTCard *cp; err=trader.ref().getNext(cp,30); if (err.isOk()) { // got a card, read it card=cp; rv=handleCard(args, card); if (args.beep) { switch(rv) { case RV_OK: fprintf(stderr, "\007"); break; case RV_HARDWARE: fprintf(stderr, "\007"); usleep(250000); fprintf(stderr, "\007"); usleep(250000); fprintf(stderr, "\007"); break; case RV_NOKVK: case RV_DISC: for (i=0; i=30) { DBG_ERROR("Could not reconnect, aborting."); break; } } // if disconnected } } // if API error } // if error card=0; } // while if (KVKDaemonStop) { err=trader.ref().stop(); if (!err.isOk()) { DBG_ERROR("Error while stopping wait request (%s)\n", err.errorString().c_str()); } } DBG_INFO("Stopping KVK daemon.\n"); card=0; DBG_NOTICE("KVK daemon stopped.\n"); return 0; }