Statistics
| Branch: | Tag: | Revision:

dvbd / src / controlledconnection.cpp @ 75f759b8

History | View | Annotate | Download (8.73 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) || 
86
          controlOutput->isReady(s) || 
87
          controlInput->isReady(s));
88
}
89

    
90
std::string ControlledConnection::getType() const
91
{
92
  return "ControlledConnection";
93
}
94

    
95
bool ControlledConnection::processReady(const Select &s)
96
{
97
  if (!Connection::processReady(s))
98
    return false;
99

    
100
  if (controlOutput && controlOutput->isReady(s)) {
101
    if (!controlOutput->process())
102
      return false;
103
  }
104
  else if (controlInput && controlInput->isReady(s)) {
105
    if (!controlInput->process()) 
106
      return false;
107

    
108
    std::string line;
109
    if (!controlInput->readLine(line)) {
110
      if (controlInput->getFree() == 0)
111
        return false;                // Buffer is full, should have had a
112
                                // line by now
113
      else
114
        return true;                // Wait for line to come in
115
    }
116

    
117
    line = stripTrailingWS(line);
118

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

    
141
        SavePriority save(this, priority - 1);
142

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

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

    
186
        struct tm tm;
187
        if (strptime(dateTime.c_str(), "%Y-%m-%d %H:%M:%S", &tm) == NULL) 
188
          writeControl("ERROR SCHEDULE INVALID DATE/TIME\n");
189
        else {
190
          time_t theTime = mktime(&tm);
191

    
192
          const std::string &type = words[3];
193
          const std::string channel = unescapeWS(words[4]);
194
          bool convertToPS = toBool(words[5]);
195
          int priority = toInt(words[6]);
196
          int duration = toInt(words[7]);        // duration in seconds
197
          const std::string path = unescapeWS(words[8]);
198
          std::string extra;
199
          pid_t pid = 0;
200
          uid_t uid = 0;
201
          gid_t gid = 0;
202

    
203
          if (words[9].size() >= 2)
204
            extra = unescapeWS(words[9].substr(1, words[9].size()-2));
205
          else
206
            extra = "";
207

    
208
          if (path.empty() || path[0] != '/')
209
            writeControl("ERROR SCHEDULE MUST BE ABSOLUTE PATH\n");
210
          else if (!readCredentials(controlFD, pid, uid, gid)) 
211
            writeControl("ERROR SCHEDULE FAILED TO READ CREDENTIALS\n");
212
          else if (pid == 0)
213
            writeControl("ERROR SCHEDULE FAILED TO READ CREDENTIALS\n");
214
          else if (!accessOk(path, uid)) 
215
            writeControl("ERROR SCHEDULE PATH ACCESS ERROR\n");
216
          else if (theTime + duration < getCurrentTime()) 
217
            writeControl("ERROR SCHEDULE START TIME IN THE PAST\n");
218
          else {
219
            std::cerr << "Scheduling recording for " << theTime 
220
                      << " (now " << time(NULL) << ")\n";
221

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

    
289
    if (controlOutput->hasOverflowed()) {
290
      std::cerr << "Control output buffer overflowed. Closing connection." << std::endl;
291
      return false;
292
    }
293
  }
294

    
295
  return true;
296
}