Statistics
| Branch: | Tag: | Revision:

dvbd / src / scheduler.cpp @ 85ce188e

History | View | Annotate | Download (7.31 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
    lastJob = std::max(r.getJob() + 1, lastJob);
78
  }  
79
}
80

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

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

    
94
  o.close();
95
}
96

    
97

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

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

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

    
117
  if (recordings.empty())
118
    return false;
119

    
120
  const Recording &r = *recordings.begin();
121

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

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

    
134
    std::cout << "Scheduled recording\n"
135
              << r << std::endl;
136

    
137
    startRecording(r);
138

    
139
    recordings.pop_front();
140
    writeDB();
141
  }
142
}
143

    
144

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

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

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

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

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

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

    
188
  ConnectionList connections;
189
  ConnectionMap programs;
190

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

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

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

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

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

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

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

    
242
  // Terminate the last connections
243

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

    
247
  ::close(devNull);
248
}
249

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

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

    
258
    i++;
259
    if (i == recordings.end())
260
      break;
261

    
262
    List overlapping;
263

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

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

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

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

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

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