/**************************************************************\ 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 #include #include "DAQReadout.h" #include "HVFeedback.h" // Function prototypes void ConsoleCommand(DAQReadout *); void CCCommand(DAQReadout *); void SignalHandler(int); void CrashHandler(int); void ExitFunction(); // ================ // 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; // writev() in DAQ thread needs to be able to write at least 3 chunks if(IOV_MAX < 3) { printf("Fatal error: IOV_MAX is less than 3, cannot use writev() to write event data.\n"); exit(EXIT_FAILURE); } // 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 (revision %s) *** \n\n",__DATE__, __TIME__, REVISION); // 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); signal(SIGHUP, &CrashHandler); atexit(&ExitFunction); // Construct main instance and create mutex for thread synchronization DAQReadout dreadout; if (pthread_mutex_init(&dreadout.control_mutex, NULL) != 0) { perror("pthread_mutex_init failed"); throw; } // Create threads 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 using readline library functions to allow line editing and history capability \********************************************************************/ void ConsoleCommand(DAQReadout *m) { char *Command; while (!m->Exit) { // Assemble prompt if (m->NumBoards == 0) snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ> "); else if (m->FirstBoard == m->LastBoard) snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d> ",m->FirstBoard); else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard); // Read Command Command = readline(m->Prompt); if (Command == NULL) { m->PrintMessage("Error reading command line input\n"); continue; } if(strlen(Command)>0) add_history(Command); // Log command m->PrintMessage(MsgToLog, "CONSOLE> %s\n", Command); // Process command pthread_mutex_lock(&m->control_mutex); m->CommandControl(Command); pthread_mutex_unlock(&m->control_mutex); free(Command); } } /********************************************************************\ 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]; // 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; } // Allows immediate reuse of socket after closing (circumvents TIME_WAIT) int Value=1; if (setsockopt(ServerSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &Value, sizeof (Value)) == -1) { m->PrintMessage("Warning: Could not set server socket option SO_REUSEADDR (%s)\n", strerror(errno)); } 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; // Looping as long as client exists and program not terminated while (!m->Exit) { // Try to read command from socket memset(Command,0,sizeof(Command)); ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE); if (ReadResult==0) break; // Client not exisiting anymore if (ReadResult==-1) { if (errno!=EINTR) m->PrintMessage("Error read from socket (%s)\n", strerror(errno)); break; } if (Command[strlen(Command)-1]=='\n') Command[strlen(Command)-1]='\0'; // Remove trailing newline // Log command m->PrintMessage(MsgToConsole|MsgToLog, "SOCKET> %s\n", Command); // Process command pthread_mutex_lock(&m->control_mutex); m->CmdFromSocket = true; m->CommandControl(Command); m->CmdFromSocket = false; pthread_mutex_unlock(&m->control_mutex); } 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; } // This function will be implicitly called by exit() void ExitFunction() { remove(LOCKFILE); return; }