Further studies in pulses…messages… AND SIGNALS!
Sorry. Signals don’t rate capslock, tbh. But I shall attempt to teach you about them.
The usual includes. msg.h is my teachers’ concoction, and may be found here.
1 2 3 4 5 6 7 8 |
#include <stdio.h> #include <sys/neutrino.h> #include "msg.h" #include <stdlib.h> #include <string.h> #include <sys/netmgr.h> #include <process.h> #include <time.h> |
Gonna need some globals – the main and a couple of helpter functions both need to access the logfile, the channel id, and the timestruct.
1 2 3 |
//globals FILE *logfile; int chid; |
1 2 |
//for timestamps time_t timestruct; |
Begin!
1 |
int main(int argc, char *argv[]) { |
1 |
printf("Logger has started.\n"); |
Initialize the timestruct, cos it is no longer 1970 and I am not Dennis Ritchie.
1 |
timestruct = time(NULL); |
The void pointer – a promise of a method, soon to be revealed.
1 2 3 4 5 |
//declare functions int setupPulseAndTimer(); int setupSignalAndTimer(); int handleMessage(int header, MESSAGE theMessage, int rcvid); void handleSignal(int signalNum); |
That logfile declaration from above? Time to use it! This is nothing more than a textfile where your program is going to write some stuff down.
1 2 |
//open the log file for writing logfile = fopen(argv[1], "w"); |
A couple more variables; theMessage is a handle for data that is received from the client. rcvid will be used to tell the client which of it’s many messages you are currently replying to.
1 2 |
int rcvid; MESSAGE theMessage; |
Upon startup, this message logger will report its process id, channel id, and node descriptor, and log that info in a pidfile. As I write this I can’t help noticing that chid isn’t initialized yet. I really hope I figure out an explanation for that later.
1 |
//handle pidfile |
1 2 |
//declare the file FILE *pidfile; |
1 2 |
//get it's location from the command line char *pidFileName = malloc(strlen(argv[0])+5); |
1 2 3 4 5 |
//tack .pid on the end just in case, you should probably //wrap that in an "if not .pid, add .pid" for robustness, //but I'm living dangerously. strcpy(pidFileName, argv[0]); strcat(pidFileName, ".pid"); |
1 2 |
//open that sucker up pidfile = fopen(pidFileName, "w"); |
1 2 3 |
//write stuff down and hope you don't //get a null pointer exception from that chid fprintf(pidfile, "%d %d %d \n", ND_LOCAL_NODE, getpid(), chid); |
1 2 3 |
//close up and let it go. fclose(pidfile); free(pidFileName); |
This bit is kinda weird. These two functions are both at the bottom of this program, so you’re sending pulses and signals to yourself. Why do that? Narcissism? Shpakism? Just do it.
1 |
int pulseSet = setupPulseAndTimer(); |
1 |
int signalSet = setupSignalAndTimer(); |
1 |
printf("Pulse status: %d \n Signal status: %d \n", pulseSet, signalSet); |
This is also weird. You’re bascially announcing, “Hey, a signal might show up, and if it does, here’s what you do.” And that instruction is standing orders for the rest of the program, or til you change it.
1 |
signal(SIGUSR2, &handleSignal); |
K, with all that established, begin a for loop that will run until further notice, taking messages and reporting them in the logfile.
1 2 |
for (;;) { rcvid = MsgReceive(chid, &theMessage, sizeof (theMessage), NULL); |
Now, this part confuses me. Cause I thought that a pulse was just a message, with a rcvid of zero. But the teacher has us, apparently, handling two cases that both match the definition of “pulse” in my mind.
So, the first “if” handles rcvid == 0.
1 2 3 4 5 6 |
if (rcvid == 0) { //When pulse arrives log it along with the code that was received. fprintf(logfile, "%s Pulse received - CODE %d \n", ctime(&timestruct), theMessage.pulse.code); printf("%s: Pulse received and logged.\n", ctime(&timestruct)); } |
The second “if” redirects to a helper method that has the same big switch statement from my previous post. It also handles a “pulse”, and like I said, I’m confused. However, I swear to god this code runs and drive as written.
Take note of the timeToQuit int there. It takes the return value of the handleMessage function – if the message was a … sigh … pulse, I guess … it returns something other than zero, which terminates the loop, and the program.
1 2 3 4 5 6 7 8 9 10 |
else { int timeToQuit = handleMessage(theMessage.client.m_hdr, theMessage, rcvid); printf("%s: Message received and logged. \n", ctime(&timestruct)); if (timeToQuit != 0) { fclose(logfile); exit(EXIT_SUCCESS); } } } //end for loop |
1 2 |
return 0; } //end main |
So where are these pulses and signals coming from? Upsettingly, they are coming from within this very program.
Here’s the setup for a pulse. It is straight out of Getting Started With QNX Neutrino, which can be found here, page 152. RTFM fools.
1 |
int setupPulseAndTimer() { |
1 2 |
//Create a timer that sends a pulse and code MSG_PULSE_ALIVE //3 sec after startup and every 5 sec thereafter |
1 2 3 4 |
timer_t timerid; struct sigevent event; struct itimerspec timer; int coid; |
1 |
coid = ConnectAttach (0, 0, chid, 0, 0); |
1 2 3 4 5 |
if (coid == -1) { fprintf (stderr, "Logger: couldn’t ConnectAttach to self!\n"); perror (NULL); exit (EXIT_FAILURE); } |
1 2 |
SIGEV_PULSE_INIT(&event, coid, SIGEV_PULSE_PRIO_INHERIT, MSG_PULSE_ALIVE, 0); |
1 2 3 4 5 6 |
if (timer_create (CLOCK_REALTIME, &event, &timerid) == -1) { fprintf (stderr, "Logger: couldn’t create a timer, errno %d\n", errno); perror (NULL); exit (EXIT_FAILURE); } |
1 2 3 4 |
timer.it_value.tv_sec = 3; //three seconds after startup... timer.it_value.tv_nsec = 0; timer.it_interval.tv_sec = 5; //and every five thereafter. timer.it_interval.tv_nsec = 0; |
1 |
timer_settime (timerid, 0, &timer, NULL); |
1 2 3 |
return 0; //return zero so my error checking printf above works. } |
The signal setup works almost exactly the same, there’s like one line that’s different.
1 2 3 4 5 6 7 8 |
int setupSignalAndTimer() { //Create a timer that sends a signal SIGUSR2 2 seconds after //startup and every 7 seconds thereafter. timer_t timerid; struct sigevent event; struct itimerspec timer; int coid; |
1 2 3 4 5 6 7 |
coid = ConnectAttach (0, 0, chid, 0, 0); if (coid == -1) { fprintf (stderr, "Logger: couldn’t ConnectAttach to self!\n"); perror (NULL); exit (EXIT_FAILURE); } |
1 2 3 |
//this is the line that's different. SIGEV_SIGNAL_INIT (&event, SIGUSR2); timer_create (CLOCK_REALTIME, &event, &timerid); |
1 2 3 4 5 6 |
if (timer_create (CLOCK_REALTIME, &event, &timerid) == -1) { fprintf (stderr, "Logger: couldn’t create a timer, errno %d\n", errno); perror (NULL); exit (EXIT_FAILURE); } |
1 2 3 4 |
timer.it_value.tv_sec = 2; timer.it_value.tv_nsec = 0; timer.it_interval.tv_sec = 7; timer.it_interval.tv_nsec = 0; |
1 |
timer_settime (timerid, 0, &timer, NULL); |
1 2 |
return 0; } |
My message handler. It logs a message if a message turns up, and shuts the whole show down if a confusingly explained pulse shows up.
1 |
int handleMessage(int header, MESSAGE theMessage, int rcvid) { |
1 |
char reply[512]; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
switch(header) { //if message type is MSG_DATA, log message and replay MSG_OK case (MSG_DATA): fprintf(logfile, "%s %s \n", ctime(&timestruct), theMessage.client.m_data); printf("Message logged. \n"); strcpy(reply, "Message logged. \n"); int repBytes = sizeof(reply); MsgReply(rcvid, MSG_OK, reply, repBytes); fflush(logfile); break; //if message type is MSG_END, reply MSG_END then exit case (MSG_END): fprintf(logfile, "%s Pulse recieved - server exiting.\n", ctime(&timestruct)); MsgReply(rcvid, MSG_END, NULL, NULL); fflush(logfile); return 1; //break; //if it is neither of these, reply MSG_INVALID and log incident. default: fprintf(logfile, "Warning: invalid message format received.\n"); MsgReply(rcvid, MSG_INVALID, NULL, NULL); break; } return 0; } |
And finally, the signal handler. It’s pretty easy.
1 2 3 |
void handleSignal(int signalNum) { fflush(logfile); } |
And that’s the whole mess. It runs on my machine; I take no responsibility if it causes your computer to have a stroke instead. Here’s the finished code.