network.c
author nathan
Sun, 12 Dec 2010 11:31:54 +0100
branchtrunk
changeset 38 79b272a68eb4
parent 29 640ce9201139
permissions -rw-r--r--
fix compile without OGG library
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2009 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 #include "data.h"
    42 
    43 #define CON_TIMEOUT      30*1000   // default timeout (ms) for connect operation
    44 #define RW_TIMEOUT       30*1000   // default timeout (ms) for read/write operations
    45 #define BUFFERSIZE       128*1024  // default ringbuffer size (bytes) for async read
    46 
    47 #define NETDOWN_TIMEOUT  30        // timeout (s) for shutting down network
    48 
    49 const char *netscript=0;
    50 
    51 // -----------------------------------------------------------------------------
    52 
    53 int RunCommand(const char *cmd, const char *State, const char *Name=0)
    54 {
    55   int res=-1;
    56   if(cmd) {
    57     char *tmp;
    58     if(Name) tmp=aprintf("%s %s \"%s\"",cmd,State,*strescape(Name,"\"$"));
    59     else     tmp=aprintf("%s %s",cmd,State);
    60 
    61     d(printf("run: executing '%s'\n",tmp))
    62     res=SystemExec(tmp);
    63     free(tmp);
    64     }
    65   return res;
    66 }
    67 
    68 // -- cNetScript ---------------------------------------------------------------
    69 
    70 class cNetScript : public cThread {
    71 private:
    72   int count;
    73   bool pending;
    74 protected:
    75   virtual void Action(void);
    76 public:
    77   cNetScript(void);
    78   ~cNetScript();
    79   void Up(void);
    80   void Down(void);
    81   };
    82 
    83 cNetScript ns;
    84 
    85 cNetScript::cNetScript(void)
    86 {
    87   count=0; pending=false;
    88 }
    89 
    90 cNetScript::~cNetScript()
    91 {
    92   if(pending) Cancel(0);
    93 }
    94 
    95 void cNetScript::Up(void)
    96 {
    97   Lock();
    98   if(netscript) {
    99     if(pending) { Cancel(0); pending=false; }
   100     RunCommand(netscript,"up");
   101     count++;
   102     }
   103   Unlock();
   104 }
   105 
   106 void cNetScript::Down(void)
   107 {
   108   Lock();
   109   if(netscript) {
   110     if(--count==0) { Start(); pending=true; }
   111     }
   112   Unlock();
   113 }
   114 
   115 void cNetScript::Action(void)
   116 {
   117   d(printf("net: netscript down delay\n"))
   118   sleep(NETDOWN_TIMEOUT);
   119   Lock();
   120   RunCommand(netscript,"down");
   121   Unlock();
   122 }
   123 
   124 // -- cNetConnect --------------------------------------------------------------
   125 
   126 class cNetConnect : public cThread {
   127 private:
   128   int fd;
   129   const char *hostname;
   130   int port;
   131   cMutex conMutex;
   132   cCondVar conCond;
   133   int result;
   134 protected:
   135   virtual void Action(void);
   136   void Done(int res);
   137 public:
   138   cNetConnect(int Fd, const char *Hostname, int Port);
   139   ~cNetConnect();
   140   int Wait(int timeoutMs);
   141   };
   142 
   143 cNetConnect::cNetConnect(int Fd, const char *Hostname, int Port)
   144 {
   145   fd=Fd;
   146   hostname=Hostname;
   147   port=Port;
   148   result=0;
   149   Start();
   150 }
   151 
   152 cNetConnect::~cNetConnect()
   153 {
   154   Cancel(1);
   155 }
   156 
   157 int cNetConnect::Wait(int timeoutMs)
   158 {
   159   conMutex.Lock();
   160   if(!result) conCond.TimedWait(conMutex,timeoutMs);
   161   conMutex.Unlock();
   162   return result;
   163 }
   164 
   165 void cNetConnect::Done(int res)
   166 {
   167   conMutex.Lock();
   168   result=res;
   169   conCond.Broadcast();
   170   conMutex.Unlock();
   171 }
   172 
   173 void cNetConnect::Action(void)
   174 {
   175   d(printf("net: name lookup %s\n",hostname))
   176   struct hostent *hp=gethostbyname(hostname);
   177   if(hp) {
   178     struct sockaddr_in sin;
   179     sin.sin_port=htons(port);
   180     sin.sin_family=AF_INET;
   181     memcpy((char *)&sin.sin_addr,hp->h_addr,hp->h_length);
   182     d(printf("net: connecting to %s:%d\n",hostname,port))
   183     if(connect(fd,(struct sockaddr *)&sin,sizeof(sin))==0) {
   184       d(printf("net: connected\n"))
   185       Done(1);
   186       }
   187     else { esyslog("connect() failed: %s",strerror(errno)); Done(-1); }
   188     }
   189   else { esyslog("Unknown host '%s'",hostname); Done(-1); }
   190 }
   191 
   192 // -- cNet ---------------------------------------------------------------------
   193 
   194 cNet::cNet(int size, int ConTimeoutMs, int RwTimeoutMs)
   195 :cRingBufferLinear(size>0?size:BUFFERSIZE,1,false)
   196 {
   197   fd=-1; deferedErrno=0; count=0;
   198   connected=netup=false;
   199   rwTimeout =RwTimeoutMs  ? RwTimeoutMs :RW_TIMEOUT;
   200   conTimeout=ConTimeoutMs ? ConTimeoutMs:CON_TIMEOUT;
   201   SetTimeouts(50,50);
   202 }
   203 
   204 cNet::~cNet()
   205 {
   206   Disconnect();
   207 }
   208 
   209 void cNet::Close(void)
   210 {
   211   if(connected) {
   212     connected=false;
   213     Cancel(2);
   214     deferedErrno=0;
   215     }
   216   if(fd>=0) { close(fd); fd=-1; }
   217   Clear(); count=0;
   218 }
   219 
   220 void cNet::Disconnect(void)
   221 {
   222   Close();
   223   if(netup) { ns.Down(); netup=false; }
   224 }
   225 
   226 bool cNet::Connect(const char *hostname, const int port)
   227 {
   228   Close();
   229   fd=socket(AF_INET,SOCK_STREAM,0);
   230   if(fd>=0) {
   231     ns.Up(); netup=true;
   232     cNetConnect *con=new cNetConnect(fd,hostname,port);
   233     int res=con->Wait(conTimeout);
   234     delete con;
   235     if(res>0) {
   236       if(fcntl(fd,F_SETFL,O_NONBLOCK)>=0) {
   237         deferedErrno=0; connected=true;
   238         Start();
   239         return(true);
   240         }
   241       else esyslog("fnctl() failed: %s",strerror(errno)); 
   242       }
   243     else if(res==0) esyslog("Connection timed out"); 
   244     }
   245   else esyslog("socket() failed: %s",strerror(errno)); 
   246   Disconnect();
   247   return false;
   248 }
   249 
   250 void cNet::CopyFromBuff(unsigned char *dest, int n)
   251 {
   252   memcpy(dest,lineBuff,n);
   253   count-=n;
   254   if(count>0) memmove(lineBuff,lineBuff+n,count);
   255 }
   256 
   257 int cNet::Gets(char *dest, int len)
   258 {
   259   len--; // let room for trailing zero
   260   int c=0;
   261   while(c<len) {
   262     if(count<=0) {
   263       int r=RingRead(lineBuff,sizeof(lineBuff));
   264       if(r<0) {
   265         if(c==0) return -1;
   266         break;
   267         }
   268       count=r;
   269       }
   270     int n=0;
   271     while(n<count && n+c<len) {
   272       if(lineBuff[n]=='\n') len=0;
   273       n++;
   274       }
   275     CopyFromBuff((unsigned char *)dest,n);
   276     dest+=n; c+=n;
   277     }
   278   *dest=0;
   279   return c;
   280 }
   281 
   282 int cNet::Read(unsigned char *dest, int len)
   283 {
   284   int c=0;
   285   if(count>0) {
   286     c=count; if(c>len) c=len;
   287     CopyFromBuff(dest,c);
   288     }
   289   else {
   290     c=RingRead(dest,len);
   291     }
   292   return c;
   293 }
   294 
   295 int cNet::Write(unsigned char *dest, int len)
   296 {
   297   int t=0, r;
   298   cPoller poll(fd,true);
   299   do {
   300     if(poll.Poll(rwTimeout)) {
   301       r=write(fd,dest,len);
   302       if(r<0 && errno!=EAGAIN) {
   303         esyslog("write() failed: %s",strerror(errno));
   304         break;
   305         }
   306       dest+=r; len-=r; t+=r;
   307       }
   308     else { esyslog("Write timed out"); break; }
   309     } while(len>0);
   310   return t;
   311 }
   312 
   313 int cNet::Puts(char *dest)
   314 {
   315   return Write((unsigned char *)dest,strlen(dest));
   316 }
   317 
   318 int cNet::RingRead(unsigned char *dest, int len)
   319 {
   320   int r=0;
   321   const uchar *rd;
   322   for(;;) {
   323     if(!Available() && deferedErrno) {
   324       d(printf("net: ringbuffer empty, async read bailed out\n"))
   325       return -1;
   326       }
   327     rd=Get(r);
   328     if(rd && r>0) {
   329       if(r>len) r=len;
   330       memcpy(dest,rd,r);
   331       Del(r);
   332       return r;
   333       }
   334     }
   335 }
   336 
   337 void cNet::Action(void)
   338 {
   339   d(printf("net: async read started\n"))
   340 
   341   cPoller poll(fd,false);
   342   while(connected) {
   343     if(poll.Poll(rwTimeout)) {
   344       unsigned char buff[8192];
   345       int r=read(fd,buff,sizeof(buff));
   346       if(r>0) {
   347         int d=0;
   348         do { d+=Put(buff+d,r-d); } while(d<r && connected);
   349         }
   350       else if(r<0 && errno!=EAGAIN) {
   351         deferedErrno=errno;
   352         esyslog("read() failed: %s",strerror(errno));
   353         break;
   354         }
   355       else if(r==0) {
   356         deferedErrno=-1;
   357         d(printf("EOF from read()\n"))
   358         break;
   359         }
   360       }
   361     else {
   362       deferedErrno=-1;
   363       esyslog("Read timed out");
   364       break;
   365       }
   366     }
   367   EnableGet();
   368   connected=false;
   369 }