MythTV  master
checksetup.cpp
Go to the documentation of this file.
1 // checksetup.cpp
2 //
3 // Some functions to do simple sanity checks on the MythTV setup.
4 // CheckSetup() is currently meant for the mythtv-setup program,
5 // but the other functions could probably be called from anywhere.
6 // They all return true if any problems are found, and add to a
7 // caller-supplied QString a message describing the problem.
8 
9 #include <QDir>
10 
11 #include "mythdb.h"
12 #include "mythcorecontext.h"
13 #include "mythdate.h"
14 #include "checksetup.h"
15 
17 
18 static bool checkPath(QString path, QStringList &probs)
19 {
20  QDir dir(path);
21  if (!dir.exists())
22  {
23  probs.push_back(QObject::tr("Path \"%1\" doesn't exist.").arg(path));
24  return true;
25  }
26 
27  QFile test(path.append("/.test"));
28  if (test.open(QIODevice::WriteOnly))
29  test.remove();
30  else
31  {
32  probs.push_back(QObject::tr("Unable to create file \"%1\" - directory "
33  "is not writable?").arg(path));
34  return true;
35  }
36 
37  return false;
38 }
39 
42 
43 bool checkStoragePaths(QStringList &probs)
44 {
45  bool problemFound = false;
46 
48 
49  query.prepare("SELECT count(groupname) FROM storagegroup;");
50  if (!query.exec() || !query.next())
51  {
52  MythDB::DBError("checkStoragePaths", query);
53  return false;
54  }
55 
56  if (query.value(0).toInt() == 0)
57  {
58  QString trMesg =
59  QObject::tr("No Storage Group directories are defined. You "
60  "must add at least one directory to the Default "
61  "Storage Group where new recordings will be "
62  "stored.");
63  probs.push_back(trMesg);
64  LOG(VB_GENERAL, LOG_ERR, trMesg);
65  return true;
66  }
67 
68  query.prepare("SELECT groupname, dirname "
69  "FROM storagegroup "
70  "WHERE hostname = :HOSTNAME;");
71  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
72  if (!query.exec() || !query.isActive())
73  {
74  MythDB::DBError("checkStoragePaths", query);
75  return false;
76  }
77  if (query.size() < 1)
78  {
80  {
81  // Master backend must have a defined Default SG
82  QString trMesg =
83  QObject::tr("No Storage Group directories are defined. "
84  "You must add at least one directory to the "
85  "Default Storage Group where new recordings "
86  "will be stored.");
87  probs.push_back(trMesg);
88  LOG(VB_GENERAL, LOG_ERR, trMesg);
89  return true;
90  }
91  return false;
92  }
93 
94  QDir checkDir("");
95  QString dirname;
96  while (query.next())
97  {
98  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
99  * uses QString::fromAscii() for toString(). Explicitly convert the
100  * value using QString::fromUtf8() to prevent corruption. */
101  dirname = QString::fromUtf8(query.value(1)
102  .toByteArray().constData());
103  QStringList tokens = dirname.split(",");
104  int curToken = 0;
105  while (curToken < tokens.size())
106  {
107  checkDir.setPath(tokens[curToken]);
108  if (checkPath(tokens[curToken], probs))
109  {
110  problemFound = true;
111  }
112  curToken++;
113  }
114  }
115 
116  return problemFound;
117 }
118 
119 bool checkImageStoragePaths(QStringList &probs)
120 {
121  bool problemFound = false;
122 
123  MSqlQuery query(MSqlQuery::InitCon());
124 
125  query.prepare("SELECT groupname "
126  "FROM storagegroup "
127  "WHERE hostname = :HOSTNAME;");
128  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
129  if (!query.exec() || !query.isActive())
130  {
131  MythDB::DBError("checkImageStoragePaths", query);
132  return false;
133  }
134  if (query.size() < 1)
135  {
136  return false;
137  }
138 
139  QStringList groups;
140  while (query.next())
141  {
142  groups += query.value(0).toString();
143  }
144 
145  if (groups.contains("Videos"))
146  {
147  if (groups.contains("Fanart") &&
148  groups.contains("Coverart") &&
149  groups.contains("Screenshots") &&
150  groups.contains("Banners"))
151  problemFound = false;
152  else
153  {
154  QString trMesg =
155  QObject::tr("You have a Video Storage "
156  "Group, but have not set up "
157  "all Image Groups. If you continue, "
158  "video image downloads will be saved in "
159  "your Videos Storage Group. Do you want "
160  "to store them in their own groups?");
161  probs.push_back(trMesg);
162  LOG(VB_GENERAL, LOG_ERR, trMesg);
163  problemFound = true;
164  }
165  }
166 
167  return problemFound;
168 }
169 
170 // I keep forgetting to change the preset (starting channel) when I add cards,
171 // so this checks that the assigned channel (which may be the default of 3)
172 // actually exists. This should save a few beginner Live TV problems
173 
174 bool checkChannelPresets(QStringList &probs)
175 {
176  bool problemFound = false;
177 
178  MSqlQuery query(MSqlQuery::InitCon());
179 
180  query.prepare("SELECT cardid, startchan, sourceid, inputname, parentid"
181  " FROM capturecard;");
182 
183  if (!query.exec() || !query.isActive())
184  {
185  MythDB::DBError("checkChannelPresets", query);
186  return false;
187  }
188 
189  while (query.next())
190  {
191  int cardid = query.value(0).toInt();
192  QString startchan = query.value(1).toString();
193  int sourceid = query.value(2).toInt();
194  int parentid = query.value(4).toInt();
195 
196  // Warnings only for real devices
197  if (parentid != 0)
198  continue;
199 
200  if (0 == sourceid)
201  {
202  probs.push_back(QObject::tr("Card %1 (type %2) is not connected "
203  "to a video source.")
204  .arg(cardid).arg(query.value(3).toString()));
205  problemFound = true;
206  continue;
207  }
208 
209  if (query.value(1).toString().isEmpty()) // Logic from tv_rec.cpp
210  startchan = "3";
211 
212  MSqlQuery channelExists(MSqlQuery::InitCon());
213  QString channelQuery;
214  channelQuery = QString("SELECT chanid FROM channel"
215  " WHERE deleted IS NULL AND "
216  " channum = '%1' AND "
217  " sourceid = %2;")
218  .arg(startchan).arg(sourceid);
219  channelExists.prepare(channelQuery);
220 
221  if (!channelExists.exec() || !channelExists.isActive())
222  {
223  MythDB::DBError("checkChannelPresets", channelExists);
224  return problemFound;
225  }
226 
227  if (channelExists.size() == 0)
228  {
229  probs.push_back(QObject::tr("Card %1 (type %2) is set to start on "
230  "channel %3, which does not exist.")
231  .arg(cardid).arg(query.value(3).toString()).arg(startchan));
232  problemFound = true;
233  }
234  }
235 
236  return problemFound;
237 }
238 
239 // Check that the display names for all parent inputs are set and that
240 // the last two characters are unique.
241 
242 bool checkInputDisplayNames(QStringList &probs)
243 {
244  bool problemFound = false;
245 
246  MSqlQuery query(MSqlQuery::InitCon());
247 
248  query.prepare("SELECT count(*) total, "
249  " count(distinct right(if(displayname<>'',"
250  " displayname, NULL), 2)) uniq "
251  "FROM capturecard "
252  "WHERE parentid = 0");
253 
254  if (!query.exec() || !query.next())
255  {
256  MythDB::DBError("checkInputDisplayNames", query);
257  return false;
258  }
259 
260  int total = query.value(0).toInt();
261  int uniq = query.value(1).toInt();
262  if (uniq != total)
263  {
264  probs.push_back(QObject::tr(
265  "The display names for one or more inputs are not "
266  "sufficiently unique. They must be set and the "
267  "last two characters must be unique because some "
268  "themes use them to identify the input."));
269  problemFound = true;
270  }
271 
272  return problemFound;
273 }
274 
277 
278 bool CheckSetup(QStringList &problems)
279 {
280  return checkStoragePaths(problems)
281  || checkChannelPresets(problems)
282  || checkInputDisplayNames(problems)
283  || checkImageStoragePaths(problems);
284 }
285 
287 {
288  bool needsReminder = false;
289  MSqlQuery query(MSqlQuery::InitCon());
290 
291  query.prepare("SELECT sourceid "
292  "FROM videosource "
293  "WHERE xmltvgrabber LIKE 'tv_grab_%';");
294  if (!query.exec() || !query.isActive())
295  {
296  MythDB::DBError("needsMFDBReminder", query);
297  }
298  else if (query.size() >= 1)
299  {
300  needsReminder = true;
301  }
302 
303  return needsReminder;
304 }
305 
306 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
bool CheckSetup(QStringList &problems)
Build up a string of common problems that the user should correct in the MythTV-Setup program.
Definition: checksetup.cpp:278
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
static bool checkPath(QString path, QStringList &probs)
Check that a directory path exists and is writable.
Definition: checksetup.cpp:18
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
int size(void) const
Definition: mythdbcon.h:203
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QVariant value(int i) const
Definition: mythdbcon.h:198
bool needsMFDBReminder()
Definition: checksetup.cpp:286
bool checkImageStoragePaths(QStringList &probs)
Definition: checksetup.cpp:119
bool isActive(void) const
Definition: mythdbcon.h:204
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
bool IsMasterHost(void)
is this the same host as the master
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool checkStoragePaths(QStringList &probs)
Do the Storage Group filesystem paths exist? Are they writable? Is the Live TV filesystem large enoug...
Definition: checksetup.cpp:43
bool checkInputDisplayNames(QStringList &probs)
Definition: checksetup.cpp:242
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QString GetHostName(void)
bool checkChannelPresets(QStringList &probs)
Definition: checksetup.cpp:174