/**************************************************************\ drsdaq.cpp Main program for DRS CTX DAQ system. Global initialization, starts threads for console input and socket interface. Sebastian Commichau, Oliver Grimm \**************************************************************/ #define DEFAULT_CONFIG "../config/DRSDAQ.conf" // Default configuration file #define LOCKFILE "/tmp/CT3_DAQ_LOCK" #include #include #include #include #include #include #include "DAQReadout.h" #include "HVFeedback.h" // Function prototypes void ConsoleCommand(DAQReadout *); void CCCommand(DAQReadout *); void SignalHandler(int); void CrashHandler(int); // ================ // Main program // ================ // // Several unlikely system call failures are handled via throwing an exception. int main(int argc, char *argv[]) { char str[MAX_COM_SIZE]; pthread_t thread_ConsoleCommand, thread_CCCommand; int LockDescriptor; // Interpret command line (do before lockfile creation in case of exit()) if((argc==3 && strcmp(argv[1],"-c")!=0) || argc==2) { printf("Usage: %s [-c ] Default file is \"%s\"\n", argv[0], DEFAULT_CONFIG); exit(EXIT_SUCCESS); } // Assure only one instance of program runs (lock creator written to log file) if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) { if(errno==EEXIST) { printf("Error: Lock file already existing\n"); sprintf(str,"paste %s -s -d ' '",LOCKFILE); system(str); } else printf("Could not create lock file %s (%s)\n", LOCKFILE, strerror(errno)); exit(EXIT_FAILURE); } close(LockDescriptor); sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME >>%s",LOCKFILE,LOCKFILE,LOCKFILE); system(str); system("clear"); printf("\n************* DRS readout built %s, %s\n\n",__DATE__,__TIME__); // Set signal handlers signal(SIGUSR1, &SignalHandler); siginterrupt (SIGUSR1, true); // Set SIGUSR1 to interrupt (and not restart) blocking system calls signal(SIGQUIT, &CrashHandler); signal(SIGILL, &CrashHandler); signal(SIGABRT, &CrashHandler); signal(SIGFPE, &CrashHandler); signal(SIGSEGV, &CrashHandler); signal(SIGBUS, &CrashHandler); signal(SIGTERM, &CrashHandler); signal(SIGINT, &CrashHandler); // Construct main instance DAQReadout dreadout(argc==3 ? argv[2] : DEFAULT_CONFIG); // Create threads and mutex for thread synchronization if (pthread_mutex_init(&dreadout.control_mutex, NULL) != 0) { perror("pthread_mutex_init failed"); throw; } if ((pthread_create(&thread_ConsoleCommand, NULL, (void * (*)(void *)) ConsoleCommand,(void *) &dreadout)) != 0) { perror("pthread_create failed with console thread"); throw; } if ((pthread_create(&thread_CCCommand, NULL, (void * (*)(void *)) CCCommand,(void *) &dreadout)) != 0) { perror("pthread_create failed with socket thread"); dreadout.SocketThread = NULL; } else dreadout.SocketThread = &thread_CCCommand; // Thread should be accessible for sending signals // Wait for threads to quit pthread_join(thread_ConsoleCommand, NULL); if(dreadout.SocketThread != NULL) pthread_join(thread_CCCommand, NULL); // Destruct mutex and main instance pthread_mutex_destroy (&dreadout.control_mutex); dreadout.~DAQReadout(); // Remove lockfile if (remove(LOCKFILE)==-1) { printf("Could not remove lock file %s (%s)\n", LOCKFILE, strerror(errno)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } /********************************************************************\ ConsoleCommand thread Handle console input \********************************************************************/ void ConsoleCommand(DAQReadout *m) { char Command[MAX_COM_SIZE], Buf[MAX_COM_SIZE]; time_t Time; while (!m->Exit) { m->PrintMessage(""); // New prompt if (fgets(Command, MAX_COM_SIZE, stdin)==NULL) m->PrintMessage("Error reading command line input\n"); strftime(Buf,MAX_COM_SIZE,"%d/%m/%y %X", localtime(&(Time=time(NULL)))); m->PrintMessage(MsgToLog, "CONSOLE(%s)> %s",Buf, Command); pthread_mutex_lock(&m->control_mutex); m->CommandControl(Command, false); pthread_mutex_unlock(&m->control_mutex); } } /********************************************************************\ CCCommand thread Listen to commands from socket (Central Control) This thread will block execution in the accept() and read() socket function while waiting for a connection or data. If the exit function is invoked through keyboard command, these blocking functions are interrupted by raising the signal SIGUSR1. Testing on errno=EINTR indicates this termination. The dummy signal handler below is needed to prevent the standard thread termination occurring when this signal is received. \********************************************************************/ void CCCommand(DAQReadout *m) { int ServerSocket,ConnectionSocket,ReadResult; struct sockaddr_in SocketAddress, ClientAddress; struct hostent *ClientName; socklen_t SizeClientAddress=sizeof(ClientAddress); char Command[MAX_COM_SIZE], Buf[MAX_COM_SIZE]; time_t Time; // Set up server socket if ((ServerSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) { m->PrintMessage("Could not open server socket, no remote connection possible (%s).\n", strerror(errno)); return; } SocketAddress.sin_family = PF_INET; SocketAddress.sin_port = htons((unsigned short) m->fCCPort); SocketAddress.sin_addr.s_addr = INADDR_ANY; if (bind(ServerSocket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) { m->PrintMessage("Could not bind port to socket (%s)\n", strerror(errno)); close(ServerSocket); return; } if (listen(ServerSocket, 0) == -1) { m->PrintMessage("Could not set socket to listening (%s)\n", strerror(errno)); close(ServerSocket); return; } // Looping to wait for incoming connection while (!m->Exit) { if ((ConnectionSocket = accept(ServerSocket, (struct sockaddr *) &ClientAddress, &SizeClientAddress)) == -1) { if (errno!=EINTR) m->PrintMessage("Failed to accept incoming connection (%s)\n", strerror(errno)); close(ServerSocket); return; } ClientName = gethostbyaddr((char *) &ClientAddress.sin_addr ,sizeof(struct sockaddr_in),AF_INET); m->PrintMessage("Connected to client at %s (%s).\n", inet_ntoa(ClientAddress.sin_addr), ClientName!=NULL ? ClientName->h_name:"name unknown"); m->Socket = ConnectionSocket; while (!m->Exit) { // Looping as long as client exists memset(Command,0,sizeof(Command)); ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE); if (ReadResult==0) break; // Client no exisiting anymore if (ReadResult==-1) { if (errno!=EINTR) m->PrintMessage("Error read from socket (%s)\n", strerror(errno)); break; } strftime(Buf,MAX_COM_SIZE,"%d/%m/%y %X", localtime(&(Time=time(NULL)))); m->PrintMessage(MsgToLog,"SOCKET(%s)> %s%s",Buf, Command, Command[strlen(Command)-1]=='\n' ? "":"\n"); m->PrintMessage(MsgToConsole,"SOCKET> %s%s",Command, Command[strlen(Command)-1]=='\n' ? "":"\n"); pthread_mutex_lock(&m->control_mutex); m->CommandControl(Command, true); // Process CC command pthread_mutex_unlock(&m->control_mutex); m->PrintMessage(""); // New prompt } m->Socket = -1; m->PrintMessage("Disconnected from client.\n"); close(ConnectionSocket); } close(ServerSocket); m->PrintMessage("Server socket closed.\n"); } /********************************************************************\ Signal handlers \********************************************************************/ // Remove lock file before running default signal code void CrashHandler(int Signal) { remove(LOCKFILE); printf("Caught signal number %d. Removing lockfile and performing standard signal action. Good luck.\n",Signal); signal(Signal, SIG_DFL); raise(Signal); } // Dummy signal handler to return from blocking syscalls void SignalHandler(int Signal) { return; }