Statistics
| Branch: | Tag: | Revision:

dvbd / src / controlledconnection.cpp @ 59be6a47

History | View | Annotate | Download (8.48 KB)

1
/*
2
  Copyright 2003 John Knottenbelt
3
  
4
  This program is free software; you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation; either version 2 of the License, or
7
  (at your option) any later version.
8
 
9
  This program is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
 
14
  You should have received a copy of the GNU General Public License
15
  along with this program; if not, write to the Free Software
16
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17
*/
18

    
19
#include "config.h"
20
#include "controlledconnection.h"
21
#include "demuxer.h"
22
#include "utils.h"
23
#include "stringutil.h"
24
#include "inputbuffer.h"
25
#include "outputbuffer.h"
26
#include "scheduler.h"
27
#include "connectionmanager.h"
28

    
29
#include <iostream>
30
#include <cstring>
31
#include <vector>
32
#include <unistd.h>
33
#include <cerrno>
34
#include <ctime>
35

    
36
ControlledConnection::ControlledConnection(TunerManager *tm, Scheduler *scheduler, 
37
                                           ConnectionManager *cm,
38
                                           int controlFD, int dataFD)
39
  : Connection(tm, dataFD),
40
    controlFD(controlFD),
41
    scheduler(scheduler),
42
    cm(cm)
43
{
44
  controlInput = new InputBuffer(controlFD, 1024, 4096);
45
  controlOutput = new OutputBuffer(controlFD, 4096, 16384);
46

    
47
  controlOutput->write("Welcome to dvbd\n");
48
}
49

    
50
ControlledConnection::~ControlledConnection()
51
{
52
  delete controlOutput;
53
  delete controlInput;
54
  close(controlFD);
55
}
56

    
57
void ControlledConnection::writeControl(const std::string &str)
58
{
59
  if (!controlOutput->write(str.data(), str.size())) {
60
      std::cerr << "Client not reading data connection" << std::endl;      
61
      setRemoveMe(true);
62
  }
63
}
64

    
65
void ControlledConnection::notifyUnsubscribe(Source *s)
66
{
67
  Connection::notifyUnsubscribe(s);
68
  if (numDemuxers() && !isRetuning())
69
    writeControl( "INFO LOST SUBSCRIPTION\n");
70
}
71

    
72
void ControlledConnection::addSelectFDs(Select &s) const
73
{
74
  Connection::addSelectFDs(s);
75

    
76
  // Write data out before reading in more data
77
  if (controlOutput->getUsed() > 0) 
78
    controlOutput->addSelectFDs(s);
79
  else
80
    controlInput->addSelectFDs(s);
81
}
82

    
83
bool ControlledConnection::isReady(const Select &s) const
84
{
85
  return Connection::isReady(s) || controlOutput->isReady(s) || controlInput->isReady(s);
86
}
87

    
88
bool ControlledConnection::processReady(const Select &s)
89
{
90
  if (!Connection::processReady(s))
91
    return false;
92

    
93
  if (controlOutput && controlOutput->isReady(s)) {
94
    if (!controlOutput->process())
95
      return false;
96
  }
97
  else if (controlInput && controlInput->isReady(s)) {
98
    if (!controlInput->process()) 
99
      return false;
100

    
101
    std::string line;
102
    if (!controlInput->readLine(line)) {
103
      if (controlInput->getFree() == 0)
104
        return false;                // Buffer is full, should have had a
105
                                // line by now
106
      else
107
        return true;                // Wait for line to come in
108
    }
109

    
110
    line = stripTrailingWS(line);
111

    
112
    std::vector<std::string> words;
113
    splitString(line, ' ', words);
114
  
115
    // PRIORITY <a number>
116
    // (Used to raise or lower the priority of an existing connection)
117
    if (words[0] == "PRIORITY") {
118
      if (words.size() != 2) 
119
        writeControl("ERROR PRIORITY WRONG NUMBER OF ARGS\n");
120
      else {
121
        setPriority(toInt(words[1]));
122
        writeControl("OK\n");
123
      }
124
    }
125
    // LIST <type> <priority>
126
    else if (words[0] == "LIST") {
127
      if (words.size() != 3)
128
        writeControl( "ERROR LIST WRONG NUMBER OF ARGS\n");
129
      else {
130
        const std::string &type = words[1];
131
        int priority = toInt(words[2]);
132
        StringList channels;
133

    
134
        SavePriority save(this, priority - 1);
135

    
136
        getTunableChannels(type, priority, channels);
137
        for (StringList::iterator i = channels.begin(); i != channels.end(); i++)
138
          writeControl( " " + *i + "\n");
139
        writeControl( "OK\n");
140
      }
141
    }
142
    // TUNE <type> <channel> <priority>
143
    // (Used to tune/retune)
144
    else if (words[0] == "TUNE") {
145
      if (words.size() != 5) 
146
        writeControl("ERROR TUNE WRONG NUMBER OF ARGS (Expected 4, got " +
147
                     ::toString(words.size() - 1) + ")\n");
148
      else {
149
        const std::string &type = words[1];
150
        std::string channel = unescapeWS(words[2]);
151
        bool convertToPS = toBool(words[3]);
152
        int priority = toInt(words[4]);
153

    
154
        // Check the arguments
155
        if (!isValidType(type)) 
156
          writeControl( "ERROR TUNE INVALID TYPE\n");
157
        else if (!isValidChannel(type,channel))
158
          writeControl( "ERROR TUNE INVALID CHANNEL ("+channel+")\n");
159
        else if (!canTune(type, channel, priority)) {
160
          writeControl( "ERROR TUNE INSUFFICIENT PRIORITY ("+::toString(priority)+")\n");
161
        }
162
        else {
163
          bool result = tune(type, channel, convertToPS, priority);
164
          if (result)
165
            writeControl( "OK\n");
166
          else 
167
            writeControl( "ERROR TUNE FAILED\n");
168
        }
169
      }
170
    }
171
    // SCHEDULE <date> <time> <type> <channel> <priority> <duration> <path>
172
    else if (words[0] == "SCHEDULE") {
173
      if (words.size() != 9) 
174
        writeControl("ERROR SCHEDULE WRONG NUMBER OF ARGS (Expected 8, got " +
175
                     ::toString(words.size() - 1) + ")\n");
176
      else {
177
        std::string dateTime = words[1] + " " + words[2];
178

    
179
        struct tm tm;
180
        if (strptime(dateTime.c_str(), "%Y-%m-%d %H:%M:%S", &tm) == NULL) 
181
          writeControl("ERROR SCHEDULE INVALID DATE/TIME\n");
182
        else {
183
          time_t theTime = mktime(&tm);
184

    
185
          const std::string &type = words[3];
186
          const std::string channel = unescapeWS(words[4]);
187
          bool convertToPS = toBool(words[5]);
188
          int priority = toInt(words[6]);
189
          int duration = toInt(words[7]);        // duration in seconds
190
          const std::string path = unescapeWS(words[8]);
191
          pid_t pid = 0;
192
          uid_t uid = 0;
193
          gid_t gid = 0;
194

    
195
          // Get the ancillary data (credentials)
196
          if (path.empty() || path[0] != '/')
197
            writeControl("ERROR SCHEDULE MUST BE ABSOLUTE PATH\n");
198
          else if (!readCredentials(controlFD, pid, uid, gid)) 
199
            writeControl("ERROR SCHEDULE FAILED TO READ CREDENTIALS\n");
200
          else if (pid == 0)
201
            writeControl("ERROR SCHEDULE FAILED TO READ CREDENTIALS\n");
202
          else if (!accessOk(path, uid)) 
203
            writeControl("ERROR SCHEDULE PATH ACCESS ERROR\n");
204
          else if (theTime < getCurrentTime()) 
205
            writeControl("ERROR SCHEDULE START TIME IN THE PAST\n");
206
          else {
207
            std::cerr << "Scheduling recording for " << theTime 
208
                      << " (now " << time(NULL) << ")\n";
209

    
210
            Recording r(type, channel, convertToPS, priority, theTime, duration, path, uid);
211
            scheduler->add(r);
212
            std::cerr << "Job number is " << r.getJob() << "\n";
213
            writeControl("JOB " + ::toString(r.getJob()) + "\n");
214
            writeControl("OK\n");
215
          }
216
        }
217
      }
218
    }
219
    else if (words[0] == "CHECK") {
220
      StringList comments;
221
      scheduler->checkOverlaps(comments);
222
      for (StringList::iterator i = comments.begin(); i != comments.end(); i++)
223
        writeControl(" " + *i + "\n");
224
      writeControl("OK\n");
225
    }
226
    else if (words[0] == "REMOVEJOB") {
227
      if (words.size() != 2) 
228
        writeControl("ERROR REMOVEJOB WRONG NUMBER OF ARGS (Expected 1, got " +
229
                     ::toString(words.size() - 1) + ")\n");
230
      else {
231
        int job = toInt(words[1]);
232
        if (!scheduler->remove(job))
233
          writeControl("ERROR REMOVEJOB NO SUCH JOB\n");
234
        else 
235
          writeControl("OK\n");
236
      }
237
    }
238
    else if (words[0] == "LISTSCHEDULE") {
239
      StringList s;
240
      scheduler->getSchedule(s);
241
      for (StringList::iterator i = s.begin(); i != s.end(); i++)
242
        writeControl(" " + *i + "\n");
243
      writeControl("OK\n");
244
    }
245
    else if (words[0] == "NEXTJOBTIME") {
246
      // Return the time for the start of the next job.
247
      time_t nextJobTime;
248
      if (scheduler->getNextJobTime(nextJobTime)) {
249
        char data[256];
250
        strftime( data, 256, "%Y-%m-%d %H:%M:%S\n", localtime(&nextJobTime));
251
        writeControl(data);
252
        writeControl("OK\n");
253
      }
254
      else 
255
        writeControl("ERROR NEXTJOBTIME NO NEXT JOB\n");
256
    }
257
    else if (words[0] == "LISTCONNECTIONS") {
258
      // List the current connections and their
259
      // priorities (possibly also their user ids)
260
      // Note -- It would be great to be able
261
      // to associate a job number with a connection
262
      // (in the event that it is indeed a scheduled
263
      // connection). Is this a hint that perhaps
264
      // we need a subclass of connection for 
265
      // scheduled connections?
266
      
267
      for (ConnectionManager::const_iterator i = cm->begin(); i != cm->end(); i++) 
268
        writeControl((*i)->toString() + "\n");
269
      writeControl("OK\n");
270
    }
271
    // OTHER COMMAND
272
    else {
273
      writeControl( "ERROR UNRECOGNISED COMMAND\n");
274
    }
275

    
276
    if (controlOutput->hasOverflowed()) {
277
      std::cerr << "Control output buffer overflowed. Closing connection." << std::endl;
278
      return false;
279
    }
280
  }
281

    
282
  return true;
283
}