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