network.c
author nathan
Sat, 29 Dec 2007 14:56:02 +0100
branchtrunk
changeset 9 dc75c2890a31
parent 0 474a1293c3c0
child 22 93aaf15c145a
permissions -rw-r--r--
generate plugin version from hg identify
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <ctype.h>
    23 #include <stdlib.h>
    24 #include <stdio.h>
    25 #include <stdarg.h>
    26 #include <sys/types.h>
    27 #include <unistd.h>
    28 #include <string.h>
    29 
    30 #include <fcntl.h>
    31 #include <sys/socket.h>
    32 #include <netinet/in.h>
    33 #include <arpa/inet.h>
    34 #include <netdb.h>
    35 
    36 #include <vdr/tools.h>
    37 
    38 #include "common.h"
    39 #include "setup-mp3.h"
    40 #include "network.h"
    41 
    42 #define CON_TIMEOUT      30*1000   // default timeout (ms) for connect operation
    43 #define RW_TIMEOUT       30*1000   // default timeout (ms) for read/write operations
    44 #define BUFFERSIZE       128*1024  // default ringbuffer size (bytes) for async read
    45 
    46 #define NETDOWN_TIMEOUT  30        // timeout (s) for shutting down network
    47 
    48 const char *netscript=0;
    49 
    50 #if APIVERSNUM == 10131
    51 #error Using this plugin with vdr 1.1.31 is not recommended (may cause high cpu load during streaming)
    52 #endif
    53 
    54 // -----------------------------------------------------------------------------
    55 
    56 int RunCommand(const char *cmd, const char *State, const char *Name=0)
    57 {
    58   int res=-1;
    59   if(cmd) {
    60     char *tmp=0;
    61     if(Name)
    62 #if APIVERSNUM < 10318
    63       asprintf(&tmp,"%s %s \"%s\"",cmd,State,strescape(Name,"\"$"));
    64 #else
    65       asprintf(&tmp,"%s %s \"%s\"",cmd,State,*strescape(Name,"\"$"));
    66 #endif
    67     else asprintf(&tmp,"%s %s",cmd,State);
    68 
    69     d(printf("run: executing '%s'\n",tmp))
    70     res=SystemExec(tmp);
    71     free(tmp);
    72     }
    73   return res;
    74 }
    75 
    76 // -- cNetScript ---------------------------------------------------------------
    77 
    78 class cNetScript : public cThread {
    79 private:
    80   int count;
    81   bool pending;
    82 protected:
    83   virtual void Action(void);
    84 public:
    85   cNetScript(void);
    86   ~cNetScript();
    87   void Up(void);
    88   void Down(void);
    89   };
    90 
    91 cNetScript ns;
    92 
    93 cNetScript::cNetScript(void)
    94 {
    95   count=0; pending=false;
    96 }
    97 
    98 cNetScript::~cNetScript()
    99 {
   100   if(pending) Cancel(0);
   101 }
   102 
   103 void cNetScript::Up(void)
   104 {
   105   Lock();
   106   if(netscript) {
   107     if(pending) { Cancel(0); pending=false; }
   108     RunCommand(netscript,"up");
   109     count++;
   110     }
   111   Unlock();
   112 }
   113 
   114 void cNetScript::Down(void)
   115 {
   116   Lock();
   117   if(netscript) {
   118     if(--count==0) { Start(); pending=true; }
   119     }
   120   Unlock();
   121 }
   122 
   123 void cNetScript::Action(void)
   124 {
   125   d(printf("net: netscript down delay\n"))
   126   sleep(NETDOWN_TIMEOUT);
   127   Lock();
   128   RunCommand(netscript,"down");
   129   Unlock();
   130 }
   131 
   132 // -- cNetConnect --------------------------------------------------------------
   133 
   134 class cNetConnect : public cThread {
   135 private:
   136   int fd;
   137   const char *hostname;
   138   int port;
   139   cMutex conMutex;
   140   cCondVar conCond;
   141   int result;
   142 protected:
   143   virtual void Action(void);
   144   void Done(int res);
   145 public:
   146   cNetConnect(int Fd, const char *Hostname, int Port);
   147   ~cNetConnect();
   148   int Wait(int timeoutMs);
   149   };
   150 
   151 cNetConnect::cNetConnect(int Fd, const char *Hostname, int Port)
   152 {
   153   fd=Fd;
   154   hostname=Hostname;
   155   port=Port;
   156   result=0;
   157   Start();
   158 }
   159 
   160 cNetConnect::~cNetConnect()
   161 {
   162   Cancel(1);
   163 }
   164 
   165 int cNetConnect::Wait(int timeoutMs)
   166 {
   167   conMutex.Lock();
   168   if(!result) conCond.TimedWait(conMutex,timeoutMs);
   169   conMutex.Unlock();
   170   return result;
   171 }
   172 
   173 void cNetConnect::Done(int res)
   174 {
   175   conMutex.Lock();
   176   result=res;
   177   conCond.Broadcast();
   178   conMutex.Unlock();
   179 }
   180 
   181 void cNetConnect::Action(void)
   182 {
   183   d(printf("net: name lookup %s\n",hostname))
   184   struct hostent *hp=gethostbyname(hostname);
   185   if(hp) {
   186     struct sockaddr_in sin;
   187     sin.sin_port=htons(port);
   188     sin.sin_family=AF_INET;
   189     memcpy((char *)&sin.sin_addr,hp->h_addr,hp->h_length);
   190     d(printf("net: connecting to %s:%d\n",hostname,port))
   191     if(connect(fd,(struct sockaddr *)&sin,sizeof(sin))==0) {
   192       d(printf("net: connected\n"))
   193       Done(1);
   194       }
   195     else { esyslog("connect() failed: %s",strerror(errno)); Done(-1); }
   196     }
   197   else { esyslog("Unknown host '%s'",hostname); Done(-1); }
   198 }
   199 
   200 // -- cNet ---------------------------------------------------------------------
   201 
   202 cNet::cNet(int size, int ConTimeoutMs, int RwTimeoutMs)
   203 :cRingBufferLinear(size>0?size:BUFFERSIZE,1,false)
   204 {
   205   fd=-1; deferedErrno=0; count=0;
   206   connected=netup=false;
   207   rwTimeout =RwTimeoutMs  ? RwTimeoutMs :RW_TIMEOUT;
   208   conTimeout=ConTimeoutMs ? ConTimeoutMs:CON_TIMEOUT;
   209 #if APIVERSNUM >= 10132
   210   SetTimeouts(50,50);
   211 #endif
   212 }
   213 
   214 cNet::~cNet()
   215 {
   216   Disconnect();
   217 }
   218 
   219 void cNet::Close(void)
   220 {
   221   if(connected) {
   222     connected=false;
   223     Cancel(2);
   224     deferedErrno=0;
   225     }
   226   if(fd>=0) { close(fd); fd=-1; }
   227   Clear(); count=0;
   228 }
   229 
   230 void cNet::Disconnect(void)
   231 {
   232   Close();
   233   if(netup) { ns.Down(); netup=false; }
   234 }
   235 
   236 bool cNet::Connect(const char *hostname, const int port)
   237 {
   238   Close();
   239   fd=socket(AF_INET,SOCK_STREAM,0);
   240   if(fd>=0) {
   241     ns.Up(); netup=true;
   242     cNetConnect *con=new cNetConnect(fd,hostname,port);
   243     int res=con->Wait(conTimeout);
   244     delete con;
   245     if(res>0) {
   246       if(fcntl(fd,F_SETFL,O_NONBLOCK)>=0) {
   247         deferedErrno=0; connected=true;
   248         Start();
   249         return(true);
   250         }
   251       else esyslog("fnctl() failed: %s",strerror(errno)); 
   252       }
   253     else if(res==0) esyslog("Connection timed out"); 
   254     }
   255   else esyslog("socket() failed: %s",strerror(errno)); 
   256   Disconnect();
   257   return false;
   258 }
   259 
   260 void cNet::CopyFromBuff(unsigned char *dest, int n)
   261 {
   262   memcpy(dest,lineBuff,n);
   263   count-=n;
   264   if(count>0) memmove(lineBuff,lineBuff+n,count);
   265 }
   266 
   267 int cNet::Gets(char *dest, int len)
   268 {
   269   len--; // let room for trailing zero
   270   int c=0;
   271   while(c<len) {
   272     if(count<=0) {
   273       int r=RingRead(lineBuff,sizeof(lineBuff));
   274       if(r<0) {
   275         if(c==0) return -1;
   276         break;
   277         }
   278       count=r;
   279       }
   280     int n=0;
   281     while(n<count && n+c<len) {
   282       if(lineBuff[n]=='\n') len=0;
   283       n++;
   284       }
   285     CopyFromBuff((unsigned char *)dest,n);
   286     dest+=n; c+=n;
   287     }
   288   *dest=0;
   289   return c;
   290 }
   291 
   292 int cNet::Read(unsigned char *dest, int len)
   293 {
   294   int c=0;
   295   if(count>0) {
   296     c=count; if(c>len) c=len;
   297     CopyFromBuff(dest,c);
   298     }
   299   else {
   300     c=RingRead(dest,len);
   301     }
   302   return c;
   303 }
   304 
   305 int cNet::Write(unsigned char *dest, int len)
   306 {
   307   int t=0, r;
   308   cPoller poll(fd,true);
   309   do {
   310     if(poll.Poll(rwTimeout)) {
   311       r=write(fd,dest,len);
   312       if(r<0 && errno!=EAGAIN) {
   313         esyslog("write() failed: %s",strerror(errno));
   314         break;
   315         }
   316       dest+=r; len-=r; t+=r;
   317       }
   318     else { esyslog("Write timed out"); break; }
   319     } while(len>0);
   320   return t;
   321 }
   322 
   323 int cNet::Puts(char *dest)
   324 {
   325   return Write((unsigned char *)dest,strlen(dest));
   326 }
   327 
   328 int cNet::RingRead(unsigned char *dest, int len)
   329 {
   330   int r=0;
   331   const uchar *rd;
   332   for(;;) {
   333     if(!Available() && deferedErrno) {
   334       d(printf("net: ringbuffer empty, async read bailed out\n"))
   335       return -1;
   336       }
   337     rd=Get(r);
   338     if(rd && r>0) {
   339       if(r>len) r=len;
   340       memcpy(dest,rd,r);
   341       Del(r);
   342       return r;
   343       }
   344     }
   345 }
   346 
   347 void cNet::Action(void)
   348 {
   349   d(printf("net: async read started\n"))
   350 
   351   cPoller poll(fd,false);
   352   while(connected) {
   353     if(poll.Poll(rwTimeout)) {
   354       unsigned char buff[8192];
   355       int r=read(fd,buff,sizeof(buff));
   356       if(r>0) {
   357         int d=0;
   358         do { d+=Put(buff+d,r-d); } while(d<r && connected);
   359         }
   360       else if(r<0 && errno!=EAGAIN) {
   361         deferedErrno=errno;
   362         esyslog("read() failed: %s",strerror(errno));
   363         break;
   364         }
   365       else if(r==0) {
   366         deferedErrno=-1;
   367         d(printf("EOF from read()\n"))
   368         break;
   369         }
   370       }
   371     else {
   372       deferedErrno=-1;
   373       esyslog("Read timed out");
   374       break;
   375       }
   376     }
   377   EnableGet();
   378   connected=false;
   379 }