source: hvcontrol/hvcontrol.cpp@ 168

Last change on this file since 168 was 161, checked in by ogrimm, 15 years ago
Removed local configuration, now dependent on Evidence configuration server
File size: 10.4 KB
Line 
1
2/**************************************************************\
3
4 Main program for the CTX G-APD HV supply, sends commands,
5 reads and monitors status of the G-APD HV supply
6
7 Sebastian Commichau, Sabrina Stark, Oliver Grimm
8
9\**************************************************************/
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <pthread.h>
15#include <signal.h>
16#include <sys/socket.h>
17#include <netdb.h>
18#include <arpa/inet.h>
19
20#include "ProcessIO.h"
21
22#include <readline/readline.h>
23#include <readline/history.h>
24
25#define DEFAULT_CONFIG "../config/HV.conf" // Default configuration file
26#define LOCKFILE "/tmp/CTX_HV_LOCK"
27
28// Function prototypes
29void ConsoleCommand(ProcessIO *);
30void CCCommand(ProcessIO *);
31void HVMonitor(ProcessIO *);
32void DummyHandler(int);
33void CrashHandler(int);
34void ExitFunction();
35
36// ================
37// Main program
38// ================
39//
40// Several unlikely system call failures are handled via throwing an exception.
41
42int main(int argc, char *argv[]) {
43
44 char str[MAX_COM_SIZE];
45 pthread_t thread_ConsoleCommand,thread_HVMonitor,thread_CCCommand;
46 int LockDescriptor;
47
48 // Interpret command line (do before lockfile creation in case of exit())
49 if((argc==3 && strcmp(argv[1],"-c")!=0) || argc==2) {
50 printf("Usage: %s [-c <ConfigFile>] Default file is \"%s\"\n", argv[0], DEFAULT_CONFIG);
51 exit(EXIT_SUCCESS);
52 }
53
54 // Assure only one instance of the HV control program runs
55 // The flag O_EXCL together with O_CREAT assure that the lock
56 // file cannot be opened by another instance, i.e. there are no parallel write accesses
57 if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL,S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) {
58 if(errno==EEXIST) {
59 printf("Error: Lock file already existing\n");
60 sprintf(str,"paste %s -s -d ' '",LOCKFILE);
61 system(str);
62 }
63 else printf("Could not create lock file %s (%s)\n", LOCKFILE, strerror(errno));
64 exit(EXIT_FAILURE);
65 }
66 close(LockDescriptor);
67 sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME>>%s",LOCKFILE,LOCKFILE,LOCKFILE);
68 system(str);
69
70 system("clear");
71 printf("\n*** HV Control built %s, %s (S.Commichau, S.Stark, O.Grimm) ***\n\n",__DATE__,__TIME__);
72
73 // Install signal handler and set signal SIGUSR1 to interrupt blocking system calls
74 signal(SIGUSR1, &DummyHandler);
75 siginterrupt (SIGUSR1, true);
76
77 // Assure lock file is deleted in case of a program crash or call to exit()
78 signal(SIGILL, &CrashHandler);
79 signal(SIGABRT, &CrashHandler);
80 signal(SIGFPE, &CrashHandler);
81 signal(SIGSEGV, &CrashHandler);
82 signal(SIGBUS, &CrashHandler);
83 atexit(&ExitFunction);
84
85 // Construct main instance and create mutex for thread synchronization
86 ProcessIO pio(argc==3 ? argv[2] : DEFAULT_CONFIG);
87 if (pthread_mutex_init(&pio.control_mutex, NULL) != 0) {
88 perror("pthread_mutex_init failed");
89 throw;
90 }
91
92 // These signals were set during construction of EvidenceServer
93 signal(SIGQUIT, &CrashHandler); // CTRL-Backspace
94 signal(SIGINT, &CrashHandler); // CTRL-C
95 signal(SIGHUP, &CrashHandler); // Terminal closed
96 signal(SIGTERM, &CrashHandler);
97
98 // Create threads
99 if ((pthread_create(&thread_ConsoleCommand, NULL, (void * (*)(void *)) ConsoleCommand,(void *) &pio)) != 0) {
100 perror("pthread_create failed with console thread");
101 throw;
102 }
103 if ((pthread_create(&thread_HVMonitor, NULL, (void * (*)(void *)) HVMonitor,(void *) &pio)) != 0) {
104 perror("pthread_create failed with HVMonitor thread");
105 throw;
106 }
107 if ((pthread_create(&thread_CCCommand, NULL, (void * (*)(void *)) CCCommand,(void *) &pio)) != 0) {
108 perror("pthread_create failed with socket thread");
109 throw;
110 }
111
112 // Threads should be accessible for sending signals
113 pio.HVMonitor = thread_HVMonitor;
114 pio.SocketThread = thread_CCCommand;
115
116 // Wait for threads to quit
117 pthread_join(thread_CCCommand, NULL);
118 pthread_join(thread_ConsoleCommand, NULL);
119 pthread_join(thread_HVMonitor, NULL);
120
121 // Destruct mutex and main instance
122 pthread_mutex_destroy (&pio.control_mutex);
123 pio.~ProcessIO();
124
125 // Remove lockfile
126 if (remove(LOCKFILE)==-1) {
127 sprintf(str, "Could not remove lock file %s", LOCKFILE);
128 perror(str);
129 exit(EXIT_FAILURE);
130 }
131
132 exit(EXIT_SUCCESS);
133}
134
135
136/********************************************************************\
137
138 ConsoleCommand thread
139
140 Handle console input using readline library functions to allow
141 line editing and history capability
142
143\********************************************************************/
144
145void ConsoleCommand(ProcessIO *m) {
146
147 char *Command;
148
149 while (!m->Exit) {
150
151 // Assemble prompt
152 snprintf(m->Prompt, sizeof(m->Prompt),"\rHV");
153 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
154 else {
155 if (m->FirstChain == m->LastChain) sprintf(m->Prompt+strlen(m->Prompt),"|C%d",m->FirstChain);
156 else sprintf(m->Prompt+strlen(m->Prompt),"|C%d-%d",m->FirstChain,m->LastChain);
157
158 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
159 else if (m->FirstBoard == m->LastBoard) sprintf(m->Prompt+strlen(m->Prompt),"|B%d> ",m->FirstBoard);
160 else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard);
161 }
162
163 // Read Command
164 Command = readline(m->Prompt);
165 if (Command==NULL) {
166 m->PrintMessage("Error reading command line input\n");
167 continue;
168 }
169 if(strlen(Command)>0) add_history(Command);
170
171 // Log command
172 m->PrintMessage(Log, "CONSOLE> %s\n", Command);
173
174 // Process command
175 pthread_mutex_lock(&m->control_mutex);
176 m->CommandControl(Command);
177 pthread_mutex_unlock(&m->control_mutex);
178
179 free(Command);
180 }
181}
182
183
184/********************************************************************\
185
186 CCCommand thread
187
188 Listen to commands from socket (Central Control)
189
190 This thread will block execution in the accept() and read() socket function
191 while waiting for a connection or data. If the exit function is invoked through
192 keyboard command, these blocking functions are interrupted by raising the signal
193 SIGUSR1. Testing on errno=EINTR indicates this termination. The dummy signal
194 handler below is needed to prevent the standard thread termination occurring
195 when this signal is received.
196
197\********************************************************************/
198
199
200void CCCommand(ProcessIO *m) {
201
202 int ServerSocket,ConnectionSocket,ReadResult;
203 struct sockaddr_in SocketAddress, ClientAddress;
204 struct hostent *ClientName;
205 socklen_t SizeClientAddress=sizeof(ClientAddress);
206 char Command[MAX_COM_SIZE];
207
208 // Set up server socket
209 if ((ServerSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
210 m->PrintMessage(All, "Could not open server socket, no remote connection possible (%s).\n", strerror(errno));
211 return;
212 }
213
214 // Allows immediate reuse of socket after closing (circumvents TIME_WAIT)
215 int Value=1;
216 if (setsockopt(ServerSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &Value, sizeof (Value)) == -1) {
217 m->PrintMessage(All, "Warning: Could not set server socket option SO_REUSEADDR (%s)\n", strerror(errno));
218 }
219
220 SocketAddress.sin_family = PF_INET;
221 SocketAddress.sin_port = htons((unsigned short) m->fCCPort);
222 SocketAddress.sin_addr.s_addr = INADDR_ANY;
223
224 if (bind(ServerSocket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
225 m->PrintMessage(All, "Could not bind port to socket (%s)\n", strerror(errno));
226 close(ServerSocket);
227 return;
228 }
229 if (listen(ServerSocket, 0) == -1) {
230 m->PrintMessage(All, "Could not set socket to listening (%s)\n", strerror(errno));
231 close(ServerSocket);
232 return;
233 }
234
235 while (!m->Exit) { // Looping to wait for incoming connection
236
237 if ((ConnectionSocket = accept(ServerSocket, (struct sockaddr *) &ClientAddress, &SizeClientAddress)) == -1) {
238 if (errno!=EINTR) m->PrintMessage(All, "Failed to accept incoming connection (%s)\n", strerror(errno));
239 close(ServerSocket);
240 return;
241 }
242
243 ClientName = gethostbyaddr((char *) &ClientAddress.sin_addr ,sizeof(struct sockaddr_in),AF_INET);
244 m->PrintMessage(All, "Connected to client at %s (%s).\n", inet_ntoa(ClientAddress.sin_addr), ClientName!=NULL ? ClientName->h_name:"name unknown");
245 m->Socket = ConnectionSocket;
246
247 // Looping as long as client exists and program not terminated
248 while (!m->Exit) {
249
250 // Try to read command from socket
251 memset(Command, 0, sizeof(Command));
252 ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE);
253 if (ReadResult==0) break; // Client does not exist anymore
254 if (ReadResult==-1) {
255 if (errno!=EINTR) m->PrintMessage(All, "Error read from socket (%s)\n", strerror(errno));
256 break;
257 }
258 if (Command[strlen(Command)-1]=='\n') Command[strlen(Command)-1]='\0'; // Remove trailing newline
259
260 // Log command
261 //m->PrintMessage(Console, "SOCKET> %s\n", Command);
262 //m->PrintMessage(Log, "SOCKET> %s\n", Command);
263
264 // Process command
265 pthread_mutex_lock(&m->control_mutex);
266 m->CmdFromSocket = true;
267 m->CommandControl(Command);
268 m->CmdFromSocket = false;
269 pthread_mutex_unlock(&m->control_mutex);
270 }
271
272 m->Socket = -1;
273 m->PrintMessage(All, "Disconnected from client.\n");
274 close(ConnectionSocket);
275 }
276 close(ServerSocket);
277}
278
279
280/********************************************************************\
281
282 HVMonitor
283
284 Monitor HV board status
285 Sebastian Commichau, November 2008
286
287\********************************************************************/
288
289void HVMonitor(ProcessIO *m) {
290
291 while (!m->Exit) {
292 if (m->state == active) {
293 pthread_mutex_lock(&m->control_mutex);
294 m->Monitor();
295 pthread_mutex_unlock(&m->control_mutex);
296 }
297 usleep((unsigned long)floor(1000000./(m->NumHVBoards*m->fStatusRefreshRate)));
298 }
299}
300
301
302/********************************************************************\
303
304 Signal handlers
305
306\********************************************************************/
307
308// Remove lock file before running default signal code
309void CrashHandler(int Signal) {
310 remove(LOCKFILE);
311 printf("Caught signal number %d. Removing lockfile and performing standard signal action. Good luck.\n",Signal);
312 signal(Signal, SIG_DFL);
313 raise(Signal);
314}
315
316// Dummy signal handler to return from blocking syscalls
317void DummyHandler(int Signal) {
318 return;
319}
320
321// This function will be implicitly called by exit()
322void ExitFunction() {
323 remove(LOCKFILE);
324 return;
325}
Note: See TracBrowser for help on using the repository browser.