Copyright © 1997 The California Institute of Technology
All rights reserved.
example3.cpp
1 /*
2 ** File
3 ** example3.cpp
4 ** Copyright 1997, The California Institute of Technology
5 **
6 ** Function
7 ** Subscribe to a file type. Save the session state in the restart file
8 ** and write files to the directory named in localPath. If an email
9 ** address is provided, use it when sending messages. Also, messages are
10 ** sent to a log file in this program. If report times are provided,
11 ** and email report of newly arrived files will be sent at those times and
12 ** a final report will be sent when the program terminates normally.
13 **
14 ** Creator
15 ** J. Rector, JPL
16 **
17 ** Created
18 ** June, 1997
19 **
20 ** History
21 **
22 */
23
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <new.h>
32 #include "Fei.h"
33 #include "FeiMailReport.h"
34
35 // Prototypes
36 Fei *connectToFei (const char *fileType, const char *restartFile,
37 const char *localPath, bool restartFlag);
38 void session (Fei *fei, const char *restartFile, FeiMailReport *report);
39 void reconnect (Fei *fei, const char *restartFile);
40 void newlyArrivedFile (const char *string, FeiMailReport *report);
41 void outOfMemory (void);
42 void sigHandler (int sig);
43 void usage (const char *progName);
44
45
46 // Signals end of processing when true. See session and sigHandler
47 // functions for useage.
48 static bool shutDown = false;
49
50 // FeiMsg buffer and status - used by all functions
51 static char msg[256];
52 static int status = FEI_OK;
53
54
55 int main (int argc, char *argv[])
56 {
57 // Only defined if we end up reporting on subscription files via mail.
58 FeiMailReport *report = (FeiMailReport *)NULL;
59
60 if (argc < 2) usage (argv[0]);
61 // If the first argument is "restart", note that. The value of i is
62 // an index into the argv array. Set initially to reflect whether or
63 // not the first argument was "restart".
64 bool restartFlag = false;
65 int i = 1;
66 if (strcmp (argv[1], "restart") == 0)
67 {
68 if (argc < 3) usage (argv[0]);
69 restartFlag = true;
70 i = 2;
71 }
72
73 // Set up from command line parameters. Must have file type. If no
74 // local path, path is current working directory ".". If not email
75 // address, emailAddr is NULL. If report times and email, the reporting.
76 const char *fileType = argv[i++];
77 const char *localPath =
78 (argc> i) ? (const char *)argv[i++] : ".";
79 const char *emailAddr =
80 (argc > i) ? (const char *)argv[i++] : (const char *)NULL;
81 // Create a report to be sent to emailAddr using subject contained in msg.
82 // Reporting times are specified in argv[i].
83 if (emailAddr && argc > i)
84 {
85 sprintf (msg, "Re: FEI subscription session for \"%s\"", fileType);
86 report = new FeiMailReport (emailAddr, msg);
87 if (status = report->getStatus())
88 return (status);
89 if (status = report->setReportTime (argv[i]))
90 return (status);
91 }
92
93 // Set up signal handler for SIGINT, SIGTERM. The program responds to the
94 // signal when the variable 'shutDown' is true. Since SIGTERM is the
95 // default signal used by kill (at least on Unix systems), you stop
96 // the program with either <cntrl-c> or the following command:
97 // # kill <pid>
98 {
99 if (signal (SIGINT, sigHandler) == SIG_ERR ||
100 signal (SIGTERM, sigHandler) == SIG_ERR)
101 {
102 status = FEI_FATAL;
103 sprintf (msg, "Can't setup signal handler. %s", strerror (errno));
104 feiMsg().record (msg, status);
105 return (status);
106 }
107 }
108
109 // Replace new-handler. Called when memory can't be allocatd by
110 // 'new'.
111 set_new_handler (outOfMemory);
112
113 // Subscription programs usually run in the background, so we'll
114 // congiure FeiMsg to do the following:
115 // 1. Send messages to a log file. Log file name is <file type>.log.
116 // 2. If an email address was supplied, send some messages to
117 // that address. Initally mail is turned off & only turned on when
118 // needed later on.
119 // 3. Use FeiMsg headers so status and time values are included
120 // in each message.
121 // Our first message is a note that the program started. Since the message
122 // is time stamped in the header we know when it started.
123 feiMsg().useHeader (true);
124 {
125 size_t size = strlen (fileType) + 5;
126 char *logFile = new char[size];
127 strcpy (logFile, fileType);
128 strcat (logFile, ".log");
129 feiMsg().openLog (logFile);
130 delete [] logFile;
131 }
132 if (emailAddr)
133 {
134 sprintf (msg, "Re: FEI subscription session for \"%s\"", fileType);
135 feiMsg().setMailTo (emailAddr, msg);
136 feiMsg().setMailMessage (false);
137
138 }
139 // Uncomment the following line if you're going to run in background
140 // and don't want messages sent to stderr.
141 // feiMsg().setPrintMessage (false);
142
143 sprintf (msg, "PID %d: subscribing to file type \"%s\".\n", getpid (),
144 fileType);
145 feiMsg().record (msg, FEI_INFO);
146
147 // Create a restart file name from the file type. Syntax is
148 // <file type>.restart
149 const char *extension = ".restart";
150 size_t len = strlen (fileType) + strlen (extension);
151 char *restartFile = new char[len + 1];
152 strcpy (restartFile, fileType);
153 strcat (restartFile, extension);
154
155 // This function either connects and subscribes or restarts if the
156 // file type is "restart" and the name of a valid restart file is
157 // provided. Files received are written directory specified by localPath.
158 Fei *fei = connectToFei (fileType, (const char *)restartFile,
159 localPath, restartFlag);
160 if (!fei)
161 return (FEI_FATAL);
162
163 // Now set up a subscription session.
164 session (fei, restartFile, report);
165
166 delete [] restartFile;
167 feiMsg().record ("Normal program termination.\n", FEI_INFO);
168 return (status);
169 }
170
171 /*
172 ** Make an FEI connection and subscribe to the supplied file type.
173 ** Include a restart file name for subscription - it can be NULL.
174 ** Also, specifiy the local path where new files are placed. If the
175 ** local path is NULL, then the files are returned in memory and
176 ** accessed from their profile records.
177 */
178 Fei *connectToFei (const char *fileType, const char *restartFile,
179 const char *localPath, bool restartFlag)
180 {
181 // Create the option. Set options so files are versioned if duplicates
182 // appear.
183 Fei *fei = new Fei;
184 fei->setOptions (feiVersion);
185
186 // If the file type is "restart", then we want to restart the
187 // subscription session using the state information found in the
188 // restart file. Note: connection to the FEI server is part of the
189 // restart process, so we don't make an explicit call to connect.
190 if (restartFlag)
191 {
192 status = fei->restart (restartFile);
193 }
194 else
195 {
196 // This is the start of a new subscription session, so connect to
197 // the file type and subscribe.
198 status = fei->connect (fileType);
199 if (status == FEI_OK)
200 status = fei->subscribe (localPath, restartFile);
201 }
202 if (status)
203 {
204 delete fei;
205 return (fei = (Fei *)NULL);
206 }
207
208 return fei; // Valid connection if return is from here.
209 }
210
211 /*
212 ** This is the subscription session loop. We continue in this loop until
213 ** interrupted by a signal. There is logic for testing if we're still
214 ** connected to the server. If the connection is ever lost, we continue
215 ** to try to reconnect. When we can, we use the restart file's state
216 ** to pickup where we left off.
217 ** Note: If we're restarting a subscription we already caught-up before
218 ** we get here.
219 */
220 void session (Fei *fei, const char *restartFile, FeiMailReport *report)
221 {
222 long delay = 5; // seconds
223 FeiProfile *profile;
224 while (!shutDown)
225 {
226 // Are we still connected to the server? If not try to reconnect
227 // using the state information in the subscription session's
228 // restart file. Continue to send messages about the state of
229 // this attempt.
230 if (!fei->isConnected ())
231 {
232 reconnect (fei, restartFile);
233 continue;
234 }
235
236 // Check to see if we have any new files. If we do, save the
237 // information for reporting at intervals. We use a delay time
238 // with 'result()' to slow down the cycle time for the session
239 // loop.
240 while ((profile = fei->result (delay)) != (FeiProfile *)NULL)
241 {
242 if (profile->getStatus () == FEI_OK)
243 newlyArrivedFile (profile->getProfileString (), report);
244
245 // Need this so we test in the inner loop as well as the outer loop.
246 // Files can arrive so quickly that the queue always has something
247 // in it and we never leave inner loop in that case.
248 if (shutDown)
249 return;
250 }
251 } // end of sesson loop
252
253 // Shut down section.
254 // Disconnect from subscription and save any files in the result queue.
255 // Note: We use a short delay for result () here. We want to check
256 // for results in a timely fashion, but at the same time we don't want
257 // to use extra resources because other programs may also be trying
258 // to work or to shutdown.
259 fei->disconnect ();
260
261 delay = 2;
262 while ((profile = fei->result (delay)) != (FeiProfile *)NULL)
263 {
264 if (profile->getStatus () == FEI_OK)
265 newlyArrivedFile (profile->getProfileString (), report);
266 delete profile;
267 }
268
269 // Report's destructor will send a final report.
270 if (report)
271 delete report;
272 }
273
274 /*
275 ** If we loose the connection to the FEI server, enter this reconnect
276 ** function. Keep trying to reconnect until we do or the until the program
277 ** terminates.
278 */
279 void reconnect (Fei *fei, const char *restartFile)
280 {
281 int minSleepTime = 60; // seconds
282 int sleepTime = minSleepTime;
283 int maxSleepTime = sleepTime * 16;
284
285 while (!shutDown)
286 {
287 // If we have a mail address, turn mail on so the next messaage
288 // is sent.
289 if (feiMsg().isMailMessage ())
290 feiMsg().setMailMessage (true);
291 if (sleepTime == minSleepTime)
292 {
293 feiMsg().record ("No FEI connection. Attempting restart.\n",
294 FEI_WARN);
295 }
296 if (fei->restart (restartFile) != FEI_OK)
297 {
298 sprintf (msg, "Can't restart subscription. Next attempt in %d minutes.\n", sleepTime / 60);
299 feiMsg().record (msg, FEI_WARN);
300 sleep (sleepTime);
301 if (sleepTime < maxSleepTime)
302 sleepTime *= 2;
303 }
304 else
305 {
306 feiMsg().record ("Restarted subscription session.\n", FEI_INFO);
307 break;
308 }
309 }
310 if (feiMsg().isMailMessage ())
311 feiMsg().setMailMessage (false);
312 }
313
314 /*
315 ** Report information on a newly arrived file. Turn off mail if it's on
316 ** so we don't mail a message each time a file arrives. Use report to
317 ** group that information.
318 */
319 void newlyArrivedFile (const char *string, FeiMailReport *report)
320 {
321 feiMsg().record (string, FEI_INFO);
322 // Are we producing reports of what's been delivered. If so,
323 // add information about the file just received to the report.
324 if (report && report->getStatus () == FEI_OK)
325 report->addToReport (string);
326 }
327
328 /*
329 ** Executed when program can't allocate memory with 'new'.
330 */
331 void outOfMemory (void)
332 {
333 status = FEI_FATAL;
334 feiMsg().useHeader (false); // headers allocate more memory in heap!
335 feiMsg().record ("Memory allocation error.", status);
336 exit (status);
337 }
338
339 /*
340 ** Signal handler for SIGTERM. Sets the 'shutDown' flag to true. The next
341 ** time it's tested in function 'session' we begin to shut down saving
342 ** any files already in the queue. If we receive a signal a second time,
343 ** shutdown is immediate.
344 */
345 void sigHandler (int sig)
346 {
347 signal (sig, sigHandler);
348
349 if (shutDown)
350 {
351 feiMsg().setMailMessage (true);
352 feiMsg().record ("Executing immediate shutdown.\n", FEI_WARN);
353 exit (FEI_OK);
354 }
355 shutDown = true;
356 }
357
358 /*
359 ** Program usage
360 */
361 void usage (const char *progName)
362 {
363 fprintf (stderr, "\n%s <file type> [<local path>] ['<email address>' [<report times>]]\n\n", progName);
364 exit (FEI_INFO);
365 }
example3.cpp