Statistics
| Branch: | Tag: | Revision:

dvbd / scheduler.cpp @ a7dee730

History | View | Annotate | Download (7.1 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 dataFD = open(r.getPath().c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0644);
151
  if (dataFD == -1) {
152
    std::cerr << "Failed to open file " << r.getPath() << " for writing: " 
153
              << strerror(errno) << "\n";
154
  }
155

    
156
  Connection *c = new Connection( tm, dataFD );
157
  c->setStopTime( r.getStopTime() );
158
  c->tune(r.getType(), r.getChannel(), r.getPriority());
159
  cm->add(c);
160
}
161

    
162
bool Scheduler::overlaps(const List &l, const Recording &r) const 
163
{
164
  for (List::const_iterator i = l.begin(); i != l.end(); i++)
165
    if (i->getStartTime() <= r.getStartTime() && 
166
        r.getStartTime() <= i->getStopTime())
167
      return true;
168
  return false;
169
}
170

    
171
void Scheduler::checkOverlappingBlock(const List &l, StringList &interruptions) const
172
{
173
  FakeTunerManager fakeTM(*tm);
174
  int devNull = open("/dev/null", O_WRONLY);
175
  
176
  typedef std::list<Connection *> ConnectionList;
177
  typedef std::map<Connection *, Recording> ConnectionMap;
178

    
179
  ConnectionList connections;
180
  ConnectionMap programs;
181

    
182
  // Here would be the place to set up any existing connections
183
  // We assume that the devices are unoccupied. This might
184
  // not be true, if for example the scheduled program is
185
  // due soon and a channel is currently connected
186
  // at high priority.
187

    
188
  for (List::const_iterator i = l.begin(); i != l.end(); i++) {
189
    const Recording &r = *i;
190

    
191
    int now = r.getStartTime();
192
    
193
    // Expire old connections
194
    for (ConnectionList::iterator j = connections.begin(); j != connections.end(); ) {
195
      Connection *e = *j;
196

    
197
      if (now > e->getStopTime()) {
198
        delete e;
199
        j = connections.erase(j);
200
      }
201
      else j++;
202
    }
203
      
204
    // Add in a new connection
205
    Connection *c = new Connection( &fakeTM, dup(devNull) );
206
    programs[c] = r;
207
    c->setStopTime( r.getStopTime() );
208

    
209
    if (!c->tune(r.getType(), r.getChannel(), r.getPriority())) {
210
      interruptions.push_back( "Failed to start recording: " + r.toString() );
211
      delete c;
212
    }
213
    else {
214
      // Check to see if the new connection displaced any of the
215
      // others
216

    
217
      for (ConnectionList::iterator j = connections.begin(); j != connections.end(); ) {
218
        Connection *e = *j;
219
        
220
        if (e->getInterrupted()) {
221
          interruptions.push_back( "Recording " + r.toString() + " interrupts " + programs[e].toString() );
222
          delete e;
223
          j = connections.erase(j);
224
        }
225
        else j++;
226
      }
227

    
228
      // Add the new connection to the list
229
      connections.push_back(c);
230
    }
231
  }
232

    
233
  // Terminate the last connections
234

    
235
  for (ConnectionList::iterator j = connections.begin(); j != connections.end(); j++)
236
    delete *j;
237

    
238
  ::close(devNull);
239
}
240

    
241
void Scheduler::checkOverlaps(StringList &interruptions) const
242
{
243
  /** Find overlapping schedules and check that the it is possible
244
      to schedule them */
245

    
246
  for (List::const_iterator i = recordings.begin(); i != recordings.end(); ) {
247
    const Recording &cur = *i;
248

    
249
    i++;
250
    if (i == recordings.end())
251
      break;
252

    
253
    List overlapping;
254

    
255
    overlapping.push_back(cur);
256
    while (i != recordings.end() && overlaps(overlapping, *i)) {
257
      overlapping.push_back(*i);
258
      i++;
259
    }
260

    
261
    if (overlapping.size() > 1) {
262
      checkOverlappingBlock(overlapping, interruptions);
263
    }
264
  }
265
}
266

    
267
void Scheduler::getSchedule(StringList &s) const
268
{
269
  for (List::const_iterator i = recordings.begin(); i != recordings.end(); i++)
270
    s.push_back((*i).toString());
271
}
272

    
273
bool Scheduler::remove(int job)
274
{
275
  for (List::iterator i = recordings.begin(); i != recordings.end(); ) {
276
    if ((*i).getJob() == job) {
277
      recordings.erase(i);
278
      return true;
279
    }
280
    else i++;
281
  }
282
  return false;
283
}
284

    
285
bool Scheduler::getNextJobTime( time_t &time ) const
286
{
287
  if (recordings.empty())
288
    return false;
289

    
290
  const Recording &r = *recordings.begin();
291
  time = r.getStartTime();
292
  return true;
293
}