// daemon for teletext // - reads from the qqs device // - accept one client asking for pages // (currently adapted to Martin Buck's VideoteXt) #include #include #include #include #include #include #include #include #include #include #include #include #include #define SRV_PORT 5400 // General macros #define NPAGES 1500 // ----------------- types ---------------------------- class Rawline { public: unsigned char chap, page, sub, line; unsigned char data[40]; int number() { if (chap == 0) chap = 8; return chap << 24 | page << 16 | sub; } }; class Page { public: int number; unsigned char data[25][40]; // line 0 starts with hours, minutes, control word, arrival time Page *next; // int hour() { return data[0][0];} // int minutes() { return data[0][1];} // int control() { return data[0][2]<<8 | data[0][3];} void clear() { memset (data, 0x80, 1000); } // time stamp to check which page is newest int tstamp() { return *(int *) (data[0]+4); } void stamp(int t) { *(int *) (data[0]+4) = t; } }; // ----------------- reporting ---------------------------- // Which process... in error reports char *proc_string = "qqdaemon"; int debug = 2; // Called before error reports void report_proc(void) { fprintf (stderr, "%s: ", proc_string); } void byee(int exit_val); // ------------------ processes ----------------------- int writer_pid = 0; int reader_pid = 0; int writer_status = 0; // Kill off the writer void end_writer(void) { if (writer_pid) kill (writer_pid, SIGHUP); } void wait_for_writer_end(void) { int deadpid; /* Now wait for the writer to finish */ while ( writer_pid && ((deadpid = wait (&writer_status)) != writer_pid) && deadpid != -1) ; } void test_writer(void) { // Has the writer gone unexpectedly? if (writer_pid == 0) { fprintf (stderr, "writer has died unexpectedly\n"); byee (-1); } } // ------------------ page handling ----------------------- // bits indicating which match is required #define MIN_UNIT (1<<0) #define MIN_TEN (1<<1) #define HR_UNIT (1<<2) #define HR_TEN (1<<3) #define PG_UNIT (1<<4) #define PG_TEN (1<<5) #define PG_HUND (1<<6) // millisecond timer, starting from start of current second class ms_timer { int t0; public: reset(void) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); t0 = tv.tv_sec; } ms_timer(void) { reset(); } int read(void) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); return (tv.tv_sec - t0) * 1000 + tv.tv_usec / 1000; } }; ms_timer timer; #include "sema.C" Page *current = NULL; #include "share.C" class Pages: Shared { int *contr; Page **index; Page *pages; public: void clean(void); Pages() { contr = (int *) allocate ( 2 * sizeof(int) + 10 * sizeof(Page *) + NPAGES * sizeof(Page)); index = (Page **) (contr + 2); pages = (Page *) (index + 10); clean(); } ~Pages() { destroy (); } Page *query(int number, int mask); Page *locate(int number); void storeline(Rawline *raw); void setfreq(int freq) { contr[0] = freq; } int getfreq(void) { return contr[0]; } }; void Pages::clean(void) { int i; // clear the index for (i=0; i<10; i++) index[i] = NULL; // link all pages for (i=0; i> 24; if (mask & PG_TEN) mask32 |= 0xf00000; if (mask & PG_UNIT) mask32 |= 0x0f0000; if (mask & HR_TEN) mask32 |= 0x00f000; if (mask & HR_UNIT) mask32 |= 0x000f00; if (mask & MIN_TEN) mask32 |= 0x0000f0; if (mask & MIN_UNIT) mask32 |= 0x00000f; lasttime = 0; Page *found = NULL; Page *p = index[chap]; while (p) { int flag = (p -> number ^ number) & mask32; if (! flag) { // page satisfies criteria, is it newer ? thistime = p->tstamp(); if (thistime > lasttime) { lasttime = thistime; found = p; } } p = p -> next; } return found; } Page *Pages::locate(int number) { Page **pp = index + (number >> 24); Page *p; // pp points to the pointer while (p = *pp) { if (p->number < number) pp = &(p->next); else break; } if (! p || p -> number > number) { // insert a new page Page *q = index[0]; // the new page if (! q) { fprintf (stderr, "buffer full\n"); return NULL; } index[0] = q->next; // unlink from empty list q -> clear(); q -> number = number; // put in label q -> next = p; // insert in list *pp = q; return q; } return p; } void Pages::storeline(Rawline *raw) { static Page *active = NULL; int number = raw->number(); // most often we stay on the same page, of course if (! active || active -> number != number) active = locate(number); if (active) { memcpy(active -> data[raw->line], raw->data, 40); if (raw->line == 0) { active->stamp(timer.read()); } } } Semaphore sema(1); // the collection of pages Pages coll; void byee(int exit_val) { if (writer_pid != 0) { if (exit_val != 0) { // I am shutting down due to an error. // Shut the writer down or else it will try to access // the freed up locks end_writer(); } wait_for_writer_end(); } // If the child died or was killed show this in the exit value if (writer_status) { if (WEXITSTATUS (writer_status) || WIFSIGNALED (writer_status)){ if (debug) fprintf (stderr, "writer died badly: 0x%04x\n", writer_status); exit (-2); } } exit (exit_val); } // The signal handler void sighandler(int dummy) { static int shutting; if (shutting) { if (debug) fprintf (stderr, "%s: ALREADY SHUTTING!\n", proc_string); return; } shutting = 1; if (debug) fprintf (stderr, "%s: sighandler on signal\n", proc_string); byee (-1); } void set_handlers(void) { if (debug) fprintf (stderr, "%s: setting handlers\n", proc_string); signal (SIGHUP, sighandler); signal (SIGINT, sighandler); signal (SIGQUIT, sighandler); signal (SIGTERM, sighandler); #ifdef SIGCHLD signal (SIGCHLD, sighandler); #else #ifdef SIGCLD signal (SIGCLD, sighandler); #endif #endif } // ------------------- reader and writer --------------------- // Read from device into the buffer void reader(void) { // the current frequency static int freq; Rawline input_buffer[140], *rawline; if (debug) fprintf (stderr, "R: Entering reader\n"); // open the device, normally /dev/qqs of course int fdin = open("qqs.dev", O_RDONLY); while (1) { int lines_read = read (fdin, input_buffer, 140*44) / 44; // wait a little while (5 ms) usleep(5000); int newfreq; if (freq != (newfreq = coll.getfreq())) { if (debug) fprintf (stderr, "R: freq from %d to %d\n", freq, newfreq); if (freq == -1) break; freq = newfreq; lseek(fdin, freq, 0); usleep(30000); continue; } if (lines_read == 0) continue; rawline = input_buffer; sema.lock (0); while (lines_read--) { // junk line number > 24 if (rawline->line <= 24) coll.storeline (rawline); rawline++; } sema.unlock (0); } close (fdin); if (debug) fprintf (stderr, "R: Exiting reader\n"); } // the socket ID on which the client is connected int client; void execute(char *command) { char cmd = *command; sema.lock (0); switch (cmd) { case 'Q': // query, see if we have a page { int page, hour, min, mask; struct { char cc[2]; short page; int info; } answer; sscanf (command+2, "%3x %2x %2x %2x", &page, &hour, &min, &mask); page = page << 16 | hour << 8 | min; current = coll.query (page, mask); if (current) { answer.cc[0] = 'O', answer.cc[1] = 'K', answer.page = current -> number >> 16; answer.info = * (int *) current -> data; if (debug) fprintf (stderr, "\tW: -> %3x %2x:%2x %2x%2x\n", answer.page, current -> data[0][0], current -> data[0][1], current -> data[0][2], current -> data[0][3]); } else { answer.cc[0] = 'N', answer.cc[1] = 'O'; answer.page = 0; answer.info = 0; if (debug) fprintf (stderr, "\tW: -> NO\n"); } write (client, &answer, 8); break; } case 'G': // get (part of) page { int first, last; if (! current) { write (client, "NO\n", 4); break; } sscanf (command+2, "%3d %3d", &first, &last); write (client, current->data[0]+first, last - first + 1); break; } case 'R': // reset found current = NULL; break; case 'P': // purge collection coll.clean(); current = NULL; write (client, "OK\n", 4); break; case 'T': // tune, passed to reader { int newfreq; sscanf (command+2, "%6d", &newfreq); fprintf (stderr, "\tW: new freq %6d\n", newfreq); coll.setfreq(newfreq); coll.clean(); current = NULL; write (client, "OK\n", 4); break; } } sema.unlock (0); } class Socket { int sock; struct sockaddr_in server; public: Socket(int port) { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror ("Socket"); exit(1); } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = port; if (bind(sock, (struct sockaddr *) &server, sizeof server) < 0) { perror ("Socket bind"); exit (1); } printf ("Waiting for connect request\n"); if (listen(sock, 5) < 0) { perror ("Socket listen"); exit (1); } } int accept(void) { int len = sizeof(server); int acc = ::accept(sock, (struct sockaddr *) &server, &len); if (acc < 0) { perror ("Socket::accept"); exit (1); } return acc; } }; void writer(void) { Socket sock(SRV_PORT); int msgsock; char command[15]; for (;;) { int nread; client = sock.accept(); fprintf (stderr, "\tW: established\n"); while ((nread = read(client, command, sizeof(command))) >= 0) { if (*command < 'A' || *command > 'Z') { fprintf (stderr, "\tW: weird(%d): ", nread); for (int i=0; i<10 && command[i]; i++) fprintf (stderr, "%2x "); fprintf (stderr, "\n"); write (client, "?", 1); usleep (10000); continue; } if (debug > 1) fprintf (stderr, "\tW: request = %s\n", command); execute(command); usleep (10000); } fprintf (stderr, "\tW: closed\n"); } if (debug) fprintf (stderr, "\tW: Exiting writer\n"); } void start_reader_and_writer(void) { fflush (stdout); fflush (stderr); if ((writer_pid = fork()) == -1){ perror ("fork"); byee (-1); } else if (writer_pid == 0){ // so this is the child */ proc_string = "qqdaemon (writer)"; reader_pid = getppid(); // Never trust fork() to propogate signals - reset them set_handlers(); writer(); } else { // and this is the parent proc_string = "qqdaemon (reader)"; reader(); wait_for_writer_end(); } } // ----------------------------------------------------------- main (int argc, char **argv) { set_handlers(); start_reader_and_writer(); byee (0); }