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