1
/***************************************************************************
2
* Copyright © 2010 Jonathan Thomas <echidnaman@kubuntu.org> *
4
* This program is free software; you can redistribute it and/or *
5
* modify it under the terms of the GNU General Public License as *
6
* published by the Free Software Foundation; either version 2 of *
7
* the License or (at your option) version 3 or any later version *
8
* accepted by the membership of KDE e.V. (or its successor approved *
9
* by the membership of KDE e.V.), which shall act as a proxy *
10
* defined in Section 14 of version 3 of the license. *
12
* This program is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU General Public License for more details. *
17
* You should have received a copy of the GNU General Public License *
18
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
19
***************************************************************************/
21
#include "workerinstallprogress.h"
23
#include <QtCore/QStringBuilder>
24
#include <QtCore/QStringList>
25
#include <QtCore/QFile>
27
#include <apt-pkg/error.h>
29
#include <sys/statvfs.h>
30
#include <sys/statfs.h>
32
#include <sys/fcntl.h>
38
#include "../globals.h"
43
WorkerInstallProgress::WorkerInstallProgress(QAptWorker* parent)
46
, m_questionResponse(QVariantMap())
47
, m_startCounting(false)
49
setenv("DEBIAN_FRONTEND", "passthrough", 1);
50
setenv("DEBCONF_PIPE", "/tmp/qapt-sock", 1);
51
// TODO: apt-listchanges
52
setenv("APT_LISTCHANGES_FRONTEND", "none", 1);
55
WorkerInstallProgress::~WorkerInstallProgress()
59
pkgPackageManager::OrderResult WorkerInstallProgress::start(pkgPackageManager *pm)
61
pkgPackageManager::OrderResult res;
63
res = pm->DoInstallPreFork();
64
if (res == pkgPackageManager::Failed) {
68
int readFromChildFD[2];
69
int writeToChildFD[2];
71
//Initialize both pipes
72
if (pipe(readFromChildFD) < 0 || pipe(writeToChildFD) < 0) {
77
if (m_child_id == -1) {
79
} else if (m_child_id == 0) {
82
if (dup(writeToChildFD[0]) != 0) {
83
close(readFromChildFD[1]);
84
close(writeToChildFD[0]);
88
// close Forked stdout and the read end of the pipe
91
res = pm->DoInstallPostFork(readFromChildFD[1]);
93
// dump errors into cerr (pass it to the parent process)
96
close(readFromChildFD[0]);
97
close(writeToChildFD[1]);
98
close(readFromChildFD[1]);
99
close(writeToChildFD[0]);
104
// make it nonblocking
105
fcntl(readFromChildFD[0], F_SETFL, O_NONBLOCK);
107
// Check if the child died
109
while (waitpid(m_child_id, &ret, WNOHANG) == 0) {
110
updateInterface(readFromChildFD[0], writeToChildFD[1]);
113
close(readFromChildFD[0]);
114
close(readFromChildFD[1]);
115
close(writeToChildFD[0]);
116
close(writeToChildFD[1]);
121
void WorkerInstallProgress::updateInterface(int fd, int writeFd)
124
static char line[1024] = "";
127
// This algorithm should be improved (it's the same as the rpm one ;)
128
int len = read(fd, buf, 1);
135
if (buf[0] == '\n') {
136
const QStringList list = QString::fromUtf8(line).split(QLatin1Char(':'));
137
const QString status = list.at(0);
138
const QString package = list.at(1);
139
QString percent = list.at(2);
140
QString str = list.at(3);
142
// If str legitimately had a ':' in it (such as a package version)
143
// we need to retrieve the next string in the list.
144
if (list.count() == 5) {
145
str += QString(QLatin1Char(':') % list.at(4));
148
if (package.isEmpty() || status.isEmpty()) {
152
if (status.contains(QLatin1String("pmerror"))) {
154
args[QLatin1String("FailedItem")] = package;
155
args[QLatin1String("ErrorText")] = str;
156
emit commitError(QApt::CommitError, args);
157
} else if (status.contains(QLatin1String("pmconffile"))) {
158
// From what I understand, the original file starts after the ' character ('\'') and
159
// goes to a second ' character. The new conf file starts at the next ' and goes to
161
QStringList strList = str.split(QLatin1Char('\''));
162
QString oldFile = strList.at(1);
163
QString newFile = strList.at(2);
166
args[QLatin1String("OldConfFile")] = oldFile;
167
args[QLatin1String("NewConfFile")] = newFile;
170
QVariantMap result = askQuestion(QApt::ConfFilePrompt, args);
172
bool replaceFile = result[QLatin1String("ReplaceFile")].toBool();
175
ssize_t reply = write(writeFd, "Y\n", 2);
178
ssize_t reply = write(writeFd, "N\n", 2);
182
m_startCounting = true;
186
if (percent.contains(QLatin1Char('.'))) {
187
QStringList percentList = percent.split(QLatin1Char('.'));
188
percentage = percentList.at(0).toInt();
190
percentage = percent.toInt();
193
emit commitProgress(str, percentage);
201
// 30 frames per second
205
QVariantMap WorkerInstallProgress::askQuestion(int questionCode, const QVariantMap &args)
207
m_questionBlock = new QEventLoop;
208
connect(m_worker, SIGNAL(answerReady(const QVariantMap&)),
209
this, SLOT(setAnswer(const QVariantMap&)));
211
emit workerQuestion(questionCode, args);
212
m_questionBlock->exec(); // Process blocked, waiting for answerReady signal over dbus
214
return m_questionResponse;
217
void WorkerInstallProgress::setAnswer(const QVariantMap &answer)
219
disconnect(m_worker, SIGNAL(answerReady(const QVariantMap&)),
220
this, SLOT(setAnswer(const QVariantMap&)));
221
m_questionResponse = answer;
222
m_questionBlock->quit();