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