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 }