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 }