Statistics
| Branch: | Tag: | Revision:

dvbd / src / scheduler.cpp @ 59be6a47

History | View | Annotate | Download (7.26 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 "scheduler.h"
20
#include "select.h"
21
#include "connection.h"
22
#include "connectionmanager.h"
23
#include "tunermanager.h"
24
#include "faketunermanager.h"
25
#include "utils.h"
26

    
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
#include <fcntl.h>
30

    
31
#include <cerrno>
32
#include <cstring>
33
#include <fstream>
34
#include <ctime>
35
#include <algorithm>
36

    
37
Scheduler::Scheduler(const std::string &dbfile, TunerManager *tm, ConnectionManager *cm)
38
  : lastJob(0), tm(tm), cm(cm), dbfile(dbfile)
39
{
40
  readDB();
41
}
42

    
43
Scheduler::~Scheduler()
44
{
45
}
46

    
47
void Scheduler::add( Recording &r )
48
{
49
  r.setJob(lastJob++);
50

    
51
  // Insert into the list sorted on start time
52
  for (List::iterator i = recordings.begin(); i != recordings.end(); i++) {
53
    if (r.getStartTime() < i->getStartTime()) {
54
      recordings.insert(i, r);
55
      writeDB();
56
      return;
57
    }
58
  }
59
  recordings.push_back(r);
60
  writeDB();
61
}
62

    
63
void Scheduler::readDB()
64
{
65
  // NB: We assume that the database is stored in sorted order.
66
  //     (this is true if it was written by writeDB)
67
  std::ifstream i(dbfile.c_str());
68
  if (!i) {
69
    std::cerr << "Warning: failed to open database file " << dbfile << " for reading:\n"
70
              << strerror(errno) << "\n";
71
    writeDB();
72
  }
73
    
74
  Recording r;
75
  while (i >> r) 
76
    recordings.push_back(r);
77
}
78

    
79
void Scheduler::writeDB() const
80
{
81
  // Write the db file
82
  std::ofstream o(dbfile.c_str());
83
  if (!o) {
84
    std::cerr << "Warning: failed to open database file " << dbfile << " for writing:\n"
85
              << strerror(errno) << "\n";
86
    return;
87
  }
88

    
89
  for (List::const_iterator i = recordings.begin(); i != recordings.end(); i++) 
90
    o << *i << "\n";
91

    
92
  o.close();
93
}
94

    
95

    
96
void Scheduler::addSelectFDs(Select &s) const
97
{
98
  long tv_sec, tv_usec;
99
  if (timeUntilNextRecording(tv_sec, tv_usec))
100
    s.addAlarm(tv_sec, tv_usec);
101
}
102

    
103
bool Scheduler::isReady(const Select &) const
104
{
105
  long tv_sec, tv_usec;
106
  if (timeUntilNextRecording(tv_sec, tv_usec))
107
    return tv_sec == 0 && tv_usec == 0;
108
  return false;
109
}
110

    
111
bool Scheduler::timeUntilNextRecording( long &tv_sec, long &tv_usec ) const
112
{
113
  time_t now = time(NULL);
114

    
115
  if (recordings.empty())
116
    return false;
117

    
118
  const Recording &r = *recordings.begin();
119

    
120
  tv_usec = 0;
121
  tv_sec = std::max(0L, r.getStartTime() - now);
122
  return true;
123
}
124

    
125
void Scheduler::process()
126
{
127
  long wait_sec, wait_usec;
128
  if (timeUntilNextRecording(wait_sec, wait_usec) &&
129
      wait_sec == 0 && wait_usec == 0) {
130
    Recording r = *recordings.begin();
131

    
132
    std::cout << "Scheduled recording\n"
133
              << r << std::endl;
134

    
135
    startRecording(r);
136

    
137
    recordings.pop_front();
138
    writeDB();
139
  }
140
}
141

    
142

    
143
void Scheduler::startRecording(const Recording &r)
144
{
145
  if (!accessOk(r.getPath(), r.getUid())) {
146
    std::cerr << "Insufficient access to " << r.getPath() << "\n";
147
    return;
148
  }
149
  
150
  int openMode = O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE;
151

    
152
  // Don't bother caching if possible
153
#ifdef O_STREAMING
154
  openMode |= O_STREAMING;
155
#endif
156

    
157
  int dataFD = open(r.getPath().c_str(), openMode, 0644);
158
  if (dataFD == -1) {
159
    std::cerr << "Failed to open file " << r.getPath() << " for writing: " 
160
              << strerror(errno) << "\n";
161
  }
162

    
163
  Connection *c = new Connection( tm, dataFD );
164
  c->setStopTime( r.getStopTime() );
165
  c->tune(r.getType(), r.getChannel(), r.getConvertToPS(), r.getPriority());
166
  cm->add(c);
167
}
168

    
169
bool Scheduler::overlaps(const List &l, const Recording &r) const 
170
{
171
  for (List::const_iterator i = l.begin(); i != l.end(); i++)
172
    if (i->getStartTime() <= r.getStartTime() && 
173
        r.getStartTime() <= i->getStopTime())
174
      return true;
175
  return false;
176
}
177

    
178
void Scheduler::checkOverlappingBlock(const List &l, StringList &interruptions) const
179
{
180
  FakeTunerManager fakeTM(*tm);
181
  int devNull = open("/dev/null", O_WRONLY);
182
  
183
  typedef std::list<Connection *> ConnectionList;
184
  typedef std::map<Connection *, Recording> ConnectionMap;
185

    
186
  ConnectionList connections;
187
  ConnectionMap programs;
188

    
189
  // Here would be the place to set up any existing connections
190
  // We assume that the devices are unoccupied. This might
191
  // not be true, if for example the scheduled program is
192
  // due soon and a channel is currently connected
193
  // at high priority.
194

    
195
  for (List::const_iterator i = l.begin(); i != l.end(); i++) {
196
    const Recording &r = *i;
197

    
198
    int now = r.getStartTime();
199
    
200
    // Expire old connections
201
    for (ConnectionList::iterator j = connections.begin(); j != connections.end(); ) {
202
      Connection *e = *j;
203

    
204
      if (now > e->getStopTime()) {
205
        delete e;
206
        j = connections.erase(j);
207
      }
208
      else j++;
209
    }
210
      
211
    // Add in a new connection
212
    Connection *c = new Connection( &fakeTM, dup(devNull) );
213
    programs[c] = r;
214
    c->setStopTime( r.getStopTime() );
215

    
216
    if (!c->tune(r.getType(), r.getChannel(), r.getConvertToPS(), r.getPriority())) {
217
      interruptions.push_back( "Failed to start recording: " + r.toString() );
218
      delete c;
219
    }
220
    else {
221
      // Check to see if the new connection displaced any of the
222
      // others
223

    
224
      for (ConnectionList::iterator j = connections.begin(); j != connections.end(); ) {
225
        Connection *e = *j;
226
        
227
        if (e->getInterrupted()) {
228
          interruptions.push_back( "Recording " + r.toString() + " interrupts " + programs[e].toString() );
229
          delete e;
230
          j = connections.erase(j);
231
        }
232
        else j++;
233
      }
234

    
235
      // Add the new connection to the list
236
      connections.push_back(c);
237
    }
238
  }
239

    
240
  // Terminate the last connections
241

    
242
  for (ConnectionList::iterator j = connections.begin(); j != connections.end(); j++)
243
    delete *j;
244

    
245
  ::close(devNull);
246
}
247

    
248
void Scheduler::checkOverlaps(StringList &interruptions) const
249
{
250
  /** Find overlapping schedules and check that the it is possible
251
      to schedule them */
252

    
253
  for (List::const_iterator i = recordings.begin(); i != recordings.end(); ) {
254
    const Recording &cur = *i;
255

    
256
    i++;
257
    if (i == recordings.end())
258
      break;
259

    
260
    List overlapping;
261

    
262
    overlapping.push_back(cur);
263
    while (i != recordings.end() && overlaps(overlapping, *i)) {
264
      overlapping.push_back(*i);
265
      i++;
266
    }
267

    
268
    if (overlapping.size() > 1) {
269
      checkOverlappingBlock(overlapping, interruptions);
270
    }
271
  }
272
}
273

    
274
void Scheduler::getSchedule(StringList &s) const
275
{
276
  for (List::const_iterator i = recordings.begin(); i != recordings.end(); i++)
277
    s.push_back((*i).toString());
278
}
279

    
280
bool Scheduler::remove(int job)
281
{
282
  for (List::iterator i = recordings.begin(); i != recordings.end(); ) {
283
    if ((*i).getJob() == job) {
284
      recordings.erase(i);
285
      return true;
286
    }
287
    else i++;
288
  }
289
  return false;
290
}
291

    
292
bool Scheduler::getNextJobTime( time_t &time ) const
293
{
294
  if (recordings.empty())
295
    return false;
296

    
297
  const Recording &r = *recordings.begin();
298
  time = r.getStartTime();
299
  return true;
300
}