/************************************************************************* * Copyright 2009 Ralph Spitzner (rasp@spitzner.org) * * This file is part of Yahdr. * * Yahdr is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Yahdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Yahdr. If not, see . **************************************************************************/ #include "defs.hpp" extern struct rbuf *mypackbuf; using namespace std; int handleDefault( ehttp &obj, void *cookie ); static string recorddir,htmltemplate,dbname,channelselect; uint16_t adapter,demux_adapter; static ehttp http; DVBtcontrol *dvbctrl; CHANNEL *basechan,*chanp; sql::mysql::MySQL_Driver *driver; sql::Connection *con; sql::Statement *stmt; struct rbuf *rbufmain,*rbufrun,*rbufvid,*rbuftim; #define NUMCLIENTS 8 void setpids(CPIDS *pidlist,uint16_t *pidarr, uint16_t *numpids) { CPIDS *p; uint16_t count = 1; p = pidlist; printf("############# setpids\n"); while(p) { printf("count=%d p=%d\n",count,p->pid); if((p->type == 2) || (p->type == 27)) pidarr[0] = p->pid; else pidarr[count++] = p->pid; p = p->next; } *numpids = count; #warning above pidcount correct ? pidarr[count] = 0; } string *specialchars(unsigned char *data) { string *ret; char special[256]; int x; unsigned char *p; if (!(ret = new string)) { cout << "no mem for new string ??" << endl; exit(0); } ret->assign(""); p = data; while (*data != '\0') { x = 0; if (*data < 0x10) data++; else if (*data == 0x10) data += 3; switch (*data) { case 0x8a: case 0x0a: case 0x0d: ret->append("
"); x = 1; break; /* case 0x20: ret->append(" "); x = 1; break;*/ case 0x86: //ret->append(""); x = 1; break; case 0x87: //ret->append(""); x = 1; break; case '\'': ret->append("'"); x = 1; break; case '"': ret->append("""); x = 1; break; case '\\': ret->append("\"); x = 1; break; case '\t': for (int i = 0; i != 4; i++) ret->append(" "); x = 1; break; case '/': ret->append("/"); x = 1; break; } if ( *data > 0x7f && x == 0) { sprintf(special,"&#%d;",(unsigned char)*data); ret->append(special); } else if ( x == 0) ret->append(1,(char)*data); // printf("data = %x -> %c %c\n", *data & 0xff,*data, *data > 0x7f ? '-' : '!'); data++; //cout << "str: " << ret->data() << endl; } return ret; } int my_write_out(uint8_t *buf, int count) { key_t msgkey = MPG_MSG_KEY; static int mq_id; static uint32_t pcount; static MPG_MSG msg; static int recfile; if (mq_id == 0) mq_id = msgget(msgkey,IPC_CREAT|0666); if (count == -1) { printf("supposed to write -1 ?\n"); return -1; } memcpy(msg.data,buf,count); msg.size = count; msg.type = MSG_DATA; msg.seq = pcount ++; for (int x = 0; x != MAXCLIENTS; x++) { if (rbufrun->http_pids[x] != 0) { msg.address = x; msgsnd(mq_id,&msg,sizeof(MPG_MSG),0); } } if (rbufrun->recording == 1) { if (recfile == 0) { printf("[%d]my_write_out: opening '%s' for recording...\n",getpid(),rbufrun->recfile); recfile = open(rbufrun->recfile,O_WRONLY|O_CREAT,0666); } else write(recfile,buf,count); } else if (recfile) { if (rbufrun->recording == 0) { close(recfile); recfile = 0; } } pcount ++; return count; } uint8_t checkpid(rbuf *buffer,uint16_t pid) { uint16_t count; switch(pid) { case 0: case 1: case 16 ... 18: case 20: return 1; default: count = buffer->numpids; do { // printf("checking pid %d\n",pid); if(pid == buffer->cpids[count]) { return 0x80 + count; } } while(count-- > 0); } return false; } pid_t run(string device) { pid_t me; int dvr; int audio,video; int count; uint16_t npid1; uint8_t buffer[(1024*64)]; int c,syn,num; int oldvpid,oldapid; int shmid; key_t key; int rewr; me = fork(); if (!me) { key = 18081968; if ((shmid = shmget(key, sizeof(struct rbuf), 0666)) < 0) { perror("shmget run"); exit(1); } if ((rbufrun = (struct rbuf *)shmat(shmid, NULL, 0)) == (struct rbuf *) -1) { perror("shmat"); exit(1); } //while(1) //; if (!(dvr = open(device.data(),O_RDONLY))) { printf("[run] no dvr >%s<\n",device.data()); return false; } while (1) { count = 0; while (1) { read(dvr,buffer,1); if (count++ == GIVE_UP) { fprintf(stderr,"I can hardly believe this is MPEG-2 TS. (buffer[0] = %x..\n",buffer[0]&0xff); exit(-1); } c = buffer[0] & 0xff; syn = c == 0x47 ? 1 : 0; if (syn) { printf("c = %x\n",c); read(dvr,buffer,188); printf("next = %x\n",buffer[187] & 0xff); if (buffer[187] == 0x47) { read(dvr,&buffer,187); break; } } } printf("synced\n"); while (1) { num = read(dvr,buffer,188); if (buffer[0] != 0x47 || num != 188) { printf("resync\n"); break; } npid1 = (buffer[1] & 0x1f) << 8; npid1 |= buffer[2]; rewr = checkpid(rbufrun,npid1); // see if we want this pid buffer[1] &= 0xe0; buffer[2] = rewr; my_write_out(buffer,188); //else if(rewr > 0) //my_write_out(buffer,188); /* if (npid1 == rbufrun->vpid) { buffer[1] &= 0xe0; buffer[2] = 101; } else if (npid1 == rbufrun->apid) { buffer[1] &= 0xe0; buffer[2] = 102; }*/ } } } else return me; } /******************************************************* request handler for video port ********************************************************/ pid_t handlereq(int sock,int clnum) { pid_t me; me = fork(); if (!me) { uint8_t buffer[4096]; int len; int shmid; key_t key; int mybeat; key_t msgkey = MPG_MSG_KEY; int mq_id; MPG_MSG msg; mq_id = msgget(msgkey,0); key = 18081968; if ((shmid = shmget(key, sizeof(struct rbuf), 0666)) < 0) { printf("[%d]handlereq: shmget ",getpid()); return 0; } if ((rbufvid = (struct rbuf *)shmat(shmid, NULL, 0)) == (struct rbuf *) -1) { printf("[%d]handlereq: shmat",getpid()); return 0; } for (int i = 0; i != MAXCLIENTS; i++) { if (rbufvid->http_pids[i] == me) { mybeat = i; break; } } read(sock,buffer,1023); printf("new client\n"); sprintf((char *)buffer,"HTTP/1.1 200 OK\nContent-Type: video/mpeg\n\n"); write(sock,buffer,strlen((char *)buffer)); //fcntl(sock,O_NONBLOCK); // int flag = 1; // int result = setsockopt(sock, /* socket affected */ // IPPROTO_TCP, /* set option at TCP level */ // TCP_NODELAY, /* name of option */ // (char *) &flag, /* the cast is historical // cruft */ // sizeof(int)); /* length of option value */ // if (result < 0) // printf("TCP_NODEAY set failed\n"); while (1) { msgrcv(mq_id,&msg,sizeof(MPG_MSG),0,0); if (msg.address != clnum) continue; len = 0; //printf("write size = %d\n",msg.size); len = send(sock,&msg.data,msg.size,0); if (len < 0 ) { close(sock); printf("[%d]handlereq: client lost... (len = %d, wrote %d)\n",getpid(),msg.size,len); rbufvid->http_pids[clnum] = 0; //while(msgrcv(mq_id,&msg,sizeof(MPG_MSG),0,0) != -1) // printf("getting msgs\n"); return 0; } rbufvid->http_beats[mybeat] = time(0); } return 1; } else return me; } pid_t servvid() { pid_t me; int good; me = fork(); if (!me) { int sockfd, newsockfd, clilen; int clients; struct sockaddr_in serv_addr, cli_addr; clients = 0; sockfd = socket(AF_INET, SOCK_STREAM , 0); if (sockfd < 0) { printf("Video out sock fail\n"); return 0; } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(666); if (::bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { printf("[%d]servvid: ERROR on binding\n",getpid()); return 0; } listen(sockfd,5); clilen = sizeof(cli_addr); while (1) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t *)&clilen); if (newsockfd < 0) { printf("[%d]servvid: ERROR on accept\n",getpid()); exit(0); } good = 0; for (clients = 0; clients != MAXCLIENTS; clients++) { if (rbufmain->http_pids[clients] == 0) { rbufmain->http_pids[clients] = handlereq(newsockfd,clients); good = 1; break; } } if (good == 0) cout << "MAXCLIENTS reached no service :-) " << endl; } } else return me; } #warning werk here static sql::mysql::MySQL_Driver *cdriver; static sql::Connection *ccon; static sql::Statement *cstmt; sql::Statement *check_sql(sql::mysql::MySQL_Driver *driver,sql::Connection *con,sql::Statement *stmt) { try { cstmt = stmt; stmt->execute("use epg"); } catch (sql::SQLException &e) { cout << __FUNCTION__ ; cout << " at " << __LINE__; cout << "########## ERR: " << e.what(); cout << " (MySQL error code: " << e.getErrorCode(); cout << ", SQLState: " << e.getSQLState() << " )" << endl; cout << "re-animating sql" << endl; cdriver = sql::mysql::get_mysql_driver_instance(); ccon = cdriver->connect("tcp://127.0.0.1:3306", "fernseher", "glotze"); cstmt = ccon->createStatement(); cstmt->execute("USE epg"); } return cstmt; } static void my_handler(int sig); struct itimerval mval; void start_timer(void) { mval.it_value.tv_sec = 10; mval.it_value.tv_usec = 0 ; mval.it_interval.tv_sec = 10; mval.it_interval.tv_usec = 0; signal(SIGALRM,&my_handler); setitimer(ITIMER_REAL,&mval,0); } static void my_handler(int sig) { sql::ResultSet *res; time_t now; char buffer[256]; CHANNEL *tp,*cp; static sql::mysql::MySQL_Driver *tdriver; static sql::Connection *tcon; static sql::Statement *tstmt; int shmid; key_t key; static time_t endtime; static int target_id; int errcount; static int event,start; int rstatus,ichan; now = time(0); if (tdriver == 0) { tdriver = sql::mysql::get_mysql_driver_instance(); tcon = tdriver->connect("tcp://127.0.0.1:3306", "fernseher", "glotze"); tstmt = tcon->createStatement(); tstmt->execute("USE epg"); } key = 18081968; if (rbuftim == NULL) { if ((shmid = shmget(key, sizeof(struct rbuf), 0666)) < 0) { perror("shmget timer"); exit(1); } if ((rbuftim = (struct rbuf *)shmat(shmid, NULL, 0)) == (struct rbuf *) -1) { perror("shmat"); exit(1); } } tstmt = check_sql(tdriver,tcon,tstmt); tp = basechan; now = time(0); printf("## beat %s",ctime(&now)); for (int i = 0; i != MAXCLIENTS; i++) { if (rbuftim->http_pids[i] != 0) { //printf("%d last beat @ %s",rbuftim->http_pids[i],ctime(&rbuftim->http_beats[i])); if ((now - rbuftim->http_beats[i] >= 30) && (rbuftim->http_beats[i] != 0)) { printf("[timer]: video server [%d] idle for %d seconds, killing it....\n",(int)rbuftim->http_pids[i],(int)(now - rbuftim->http_beats[i])); kill(rbuftim->http_pids[i],SIGKILL); rbuftim->http_pids[i] = 0; rbuftim->http_beats[i] = 0; } } } if (rbuftim->recording == 0) { sprintf(buffer,"select * from recordings where start>%d and start<%d;",(int)now-240,(int)now+120); try { res = tstmt->executeQuery(buffer); } catch (sql::SQLException &e) { cout << __FUNCTION__ ; cout << " at " << __LINE__; cout << "########## ERR: " << e.what(); cout << " (MySQL error code: " << e.getErrorCode(); cout << ", SQLState: " << e.getSQLState() << " )" << endl; cout << "not ok 1" << endl; } if (res) { if (res->next() && rbuftim->recording == 0 && res->getInt("recflag") == 1) { errcount = 0; //dvbctrl->tuneto(res->getInt("freq"),res->getInt("bw"),(fe_modulation_t)res->getInt("qam")); ichan = res->getInt("intchan"); cp = basechan; while (cp) { if (cp->channum == ichan) break; cp = cp->next; } //delete sp; #warning "WERK on pid setting !!!" printf("recording, setting pids\n"); setpids(cp->cpids,rbufmain->cpids,&rbufmain->numpids); dvbctrl->tuneto(cp->freq,cp->bw,cp->cpids,cp->qam); printf("tuneto done...."); endtime = res->getInt("start") + res->getInt("duration") + 240; target_id = res->getInt("event_id"); event = res->getInt("event_id"); start = res->getInt("start"); while (1) { rstatus = checkrunning(adapter,res->getInt("event_id")); if (errcount > 20 && time(0) > res->getInt("start")) /// sender sent not defined as status 20 times, start anyway { endtime = time(0) + res->getInt("duration")+180; strcpy(rbuftim->recfile,res->getString("filename").data()); rbuftim->recording = 1; shmdt(rbuftim); return; } printf("timer: rstatus %d\n",rstatus); switch (rstatus) { case -1: case 0: errcount ++; break; case 4: //case 2: if (rbuftim->recording == 0) { endtime = time(0) + res->getInt("duration")+180; strcpy(rbuftim->recfile,res->getString("filename").data()); rbuftim->recording = 1; } return; break; } sleep(2); } } else { sprintf(buffer,"select * from recordings where start>%d and start<%d;",(int)now-30,(int)now+30); try { res = tstmt->executeQuery(buffer); } catch (sql::SQLException &e) { cout << __FUNCTION__ ; cout << "########## ERR: " << e.what(); cout << " (MySQL error code: " << e.getErrorCode(); cout << ", SQLState: " << e.getSQLState() << " )" << endl; cout << "not ok 1" << endl; } if (res->next()) { if (rbuftim->recording == 0 && res->getInt("recflag") == 0) { /// just switch channels ichan = res->getInt("intchan"); cp = basechan; while (cp) { if (cp->channum == ichan) break; cp = cp->next; } // delete sp; setpids(cp->cpids,rbufmain->cpids,&rbufmain->numpids); dvbctrl->tuneto(cp->freq,cp->bw,cp->cpids,cp->qam); //dvbctrl->tuneto(res->getInt("freq"),res->getInt("bw"),(fe_modulation_t)res->getInt(9)); sprintf(buffer,"delete from recordings where event_id=%d and start=%d", res->getInt("event_id"),res->getInt("start")); stmt->execute(buffer); } } } } } else if (rbuftim->recording == 1 && rbuftim->instant == 0) { rstatus = checkrunning(adapter,target_id); if (time(0) > endtime ) { printf("stop recording [running status %d + grace timeout(end %d now %d]\n",rstatus,(int)endtime,(int)time(0)); sprintf(buffer,"delete from recordings where event_id=%d and start=%d;",event,start); printf("SQL: %s\n",buffer); try { tstmt->execute(buffer); } catch (sql::SQLException &e) { cout << __FUNCTION__ ; cout << " at " << __LINE__; cout << "########## ERR: " << e.what(); cout << " (MySQL error code: " << e.getErrorCode(); cout << ", SQLState: " << e.getSQLState() << " )" << endl; cout << "not ok 1" << endl; } rbuftim->recording = 0; } } } #warning something stinks here killhandler calling itself ? void killhandler(int sig) { int shmid; key_t key; struct rbuf *rbufkill; key = 18081968; if ((shmid = shmget(key, sizeof(struct rbuf), 0666)) < 0) { perror("shmget timer"); exit(1); } if ((rbufkill = (struct rbuf *)shmat(shmid, NULL, 0)) == (struct rbuf *) -1) { perror("shmat"); exit(1); } printf("caught signal %d, terminating\n",sig); printf("Killing videoserver(s).."); fflush(stdout); for (int i = 0; i != MAXCLIENTS; i++) { if (rbufkill->http_pids[i] != 0) { printf("[%d]..",rbufkill->http_pids[i]); fflush(stdout); kill(rbufkill->http_pids[i],SIGINT); rbufkill->http_pids[i] = 0; } } if (rbufkill->servvid != 0) { printf("\nKilling videoport-handler [%d]\n",rbufkill->servvid); kill(rbufkill->servvid,SIGINT); rbufkill->servvid = 0; } if (rbufkill->run_pid != 0) { printf("Killing video-packethandler [%d]\n",rbufkill->run_pid); kill(rbufkill->run_pid,SIGINT); rbufkill->run_pid = 0; } printf("exiting...\n"); exit(0); } static int con_fd; void handle_http(int *s,Connection *c) { struct pollfd poller; int err; pid_t me; if (!fork()) { me = getpid(); poller.fd = *s; poller.events = POLLIN | POLLRDHUP; /// this has to be here aswell because of fork() /* driver = sql::mysql::get_mysql_driver_instance(); con = driver->connect("tcp://127.0.0.1:3306", "fernseher", "glotze"); stmt = con->createStatement(); con->setSchema("epg"); */ con_fd = *s; while (1) { printf("[%d]handle_http: polling ...\n",me); /// 300 sec. is what netscape/seamonkey usually requests, /// no sense in taking Keep-Alive: