Copyright © 1997 The California Institute of Technology
All rights reserved.
example4.cpp
1 /*
2 ** File
3 ** example4.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 "Subscription.h"
33
34 // Prototypes
35 void session (Subscription& notify);
36 void newlyArrivedFile (const char *string, FeiMailReport *report);
37 void outOfMemory (void);
38 void sigHandler (int sig);
39 void usage (const char *progName);
40
41
42 // Signals end of processing when true. See session and sigHandler
43 // functions for useage.
44 static bool shutDown = false;
45
46 // FeiMsg buffer and status - used by all functions
47 static char msg[256];
48 static int status = FEI_OK;
49
50
51 int main (int argc, char *argv[])
52 {
53 // Only defined if we end up reporting on subscription files via mail.
54 FeiMailReport *report = (FeiMailReport *)NULL;
55
56 if (argc < 2) usage (argv[0]);
57
58 // If the first argument is "restart", note that. The value of i is
59 // an index into the argv array. Set initially to reflect whether or
60 // not the first argument was "restart".
61 bool restartFlag = false;
62 int i = 1;
63 if (strcmp (argv[1], "restart") == 0)
64 {
65 if (argc < 3) usage (argv[0]);
66 restartFlag = true;
67 i = 2;
68 }
69
70 // Create a Subscription session object used for notification. Assume
71 // that argv[i] has the file type.
72 Subscription notify (argv[i++]);
73 if (status = notify.getStatus ())
74 return status;
75 notify.setOptions (feiVersion);
76
77 // Set up from command line parameters. Must have file type. If no
78 // local path, path is current working directory ".". If not email
79 // address, emailAddr is NULL. If report times and email, the reporting.
80 const char *localPath =
81 (argc> i) ? (const char *)argv[i++] : ".";
82 const char *emailAddr =
83 (argc > i) ? (const char *)argv[i++] : (const char *)NULL;
84 // Create a report to be sent to emailAddr using subject contained in msg.
85 // Reporting times are specified in argv[i].
86 if (emailAddr && argc > i)
87 if (!(report = notify.initReport (emailAddr, argv[i])))
88 return notify.getStatus ();
89
90 // Set up signal handler for SIGINT, SIGTERM. The program responds to the
91 // signal when the variable 'shutDown' is true. Since SIGTERM is the
92 // default signal used by kill (at least on Unix systems), you stop
93 // the program with either <cntrl-c> or the following command:
94 // # kill <pid>
95 {
96 if (signal (SIGINT, sigHandler) == SIG_ERR ||
97 signal (SIGTERM, sigHandler) == SIG_ERR)
98 {
99 status = FEI_FATAL;
100 sprintf (msg, "Can't setup signal handler. %s", strerror (errno));
101 feiMsg().record (msg, status);
102 return (status);
103 }
104 }
105
106 // Replace new-handler. Called when memory can't be allocatd by
107 // 'new'.
108 set_new_handler (outOfMemory);
109
110 // Set up messaging for log, display and email
111 notify.initMessages (emailAddr, true);
112 if (status = notify.getStatus ())
113 return status;
114
115 // This function either connects and subscribes or restarts if the
116 // file type is "restart" and the name of a valid restart file is
117 // provided. Files received are written directory specified by localPath.
118 if (notify.connectForSubscribe (localPath, restartFlag) > FEI_OK)
119 return (FEI_FATAL);
120
121 // Now set up a subscription session.
122 session (notify);
123
124 feiMsg().record ("Normal program termination.\n", FEI_INFO);
125 return (status);
126 }
127
128 /*
129 ** This is the subscription session loop. We continue in this loop until
130 ** interrupted by a signal. There is logic for testing if we're still
131 ** connected to the server. If the connection is ever lost, we continue
132 ** to try to reconnect. When we can, we use the restart file's state
133 ** to pickup where we left off.
134 ** Note: If we're restarting a subscription we already caught-up before
135 ** we get here.
136 */
137 void session (Subscription& notify)
138 {
139 FeiMailReport *report = notify.getReport ();
140
141 long delay = 5; // seconds
142 FeiProfile *profile;
143 while (!shutDown)
144 {
145 // Are we still connected to the server? If not try to reconnect
146 // using the state information in the subscription session's
147 // restart file. Continue to send messages about the state of
148 // this attempt.
149 if (!notify.isConnected ())
150 {
151 notify.reconnect (&shutDown);
152 continue;
153 }
154
155 // Check to see if we have any new files. If we do, save the
156 // information for reporting at intervals. We use a delay time
157 // with 'result()' to slow down the cycle time for the session
158 // loop.
159 while ((profile = notify.result (delay)) != (FeiProfile *)NULL)
160 {
161 if (profile->getStatus () == FEI_OK)
162 newlyArrivedFile (profile->getProfileString (), report);
163
164 // Need this so we test in the inner loop as well as the outer loop.
165 // Files can arrive so quickly that the queue always has something
166 // in it and we never leave inner loop in that case.
167 if (shutDown)
168 return;
169 }
170 } // end of sesson loop
171
172 // Shut down section.
173 // Disconnect from subscription and save any files in the result queue.
174 // Note: We use a short delay for result () here. We want to check
175 // for results in a timely fashion, but at the same time we don't want
176 // to use extra resources because other programs may also be trying
177 // to work or to shutdown.
178 notify.disconnect ();
179
180 delay = 2;
181 while ((profile = notify.result (delay)) != (FeiProfile *)NULL)
182 {
183 if (profile->getStatus () == FEI_OK)
184 newlyArrivedFile (profile->getProfileString (), report);
185 delete profile;
186 }
187
188 // Report's destructor will send a final report.
189 if (report)
190 delete report;
191 }
192
193 /*
194 ** Report information on a newly arrived file. Turn off mail if it's on
195 ** so we don't mail a message each time a file arrives. Use report to
196 ** group that information.
197 */
198 void newlyArrivedFile (const char *string, FeiMailReport *report)
199 {
200 feiMsg().record (string, FEI_INFO);
201 // Are we producing reports of what's been delivered. If so,
202 // add information about the file just received to the report.
203 if (report && report->getStatus () == FEI_OK)
204 report->addToReport (string);
205 }
206
207 /*
208 ** Executed when program can't allocate memory with 'new'.
209 */
210 void outOfMemory (void)
211 {
212 status = FEI_FATAL;
213 feiMsg().useHeader (false); // headers allocate more memory in heap!
214 feiMsg().record ("Memory allocation error.", status);
215 exit (status);
216 }
217
218 /*
219 ** Signal handler for SIGTERM. Sets the 'shutDown' flag to true. The next
220 ** time it's tested in function 'session' we begin to shut down saving
221 ** any files already in the queue. If we receive a signal a second time,
222 ** shutdown is immediate.
223 */
224 void sigHandler (int sig)
225 {
226 signal (sig, sigHandler);
227
228 if (shutDown)
229 {
230 feiMsg().setMailMessage (true);
231 feiMsg().record ("Executing immediate shutdown.\n", FEI_WARN);
232 exit (FEI_OK);
233 }
234 shutDown = true;
235 }
236
237 /*
238 ** Program usage
239 */
240 void usage (const char *progName)
241 {
242 fprintf (stderr, "\n%s [restart] <file type> [<local path>] ['<email address>' [<report times>]]\n\n", progName);
243 exit (FEI_INFO);
244 }
example4.cpp