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