I have made the following changes intended for : CE:MW:Shared / ssu Please review and accept or decline. BOSS has already run some checks on this request. See the "Messages from BOSS" section below.
https://build.pub.meego.com//request/show/7284 Thank You, aard [This message was auto-generated] --- Request # 7284: Messages from BOSS: State: review at 2012-11-04T18:20:55 by bossbot Reviews: accepted by bossbot : Prechecks succeeded. new for CE-maintainers : Please replace this text with a review and approve/reject the review (not the SR). BOSS will take care of the rest Changes: submit: home:aard:branches:CE:MW:Shared / ssu -> CE:MW:Shared / ssu changes files: -------------- --- ssu.changes +++ ssu.changes @@ -0,0 +1,4 @@ +* Sun Nov 04 2012 Bernd Wachter <[email protected]> - 0.14 +- Move device detection bits into configuration files +- Allow downloading authorized_keys file during rnd registration + old: ---- ssu-0.13.tar.gz new: ---- ssu-0.14.tar.gz spec files: ----------- --- ssu.spec +++ ssu.spec @@ -1,5 +1,5 @@ Name: ssu -Version: 0.13 +Version: 0.14 Release: 1 Summary: SSU enabler for RND Group: System/Base other changes: -------------- ++++++ ssu-0.13.tar.gz -> ssu-0.14.tar.gz --- board-mappings.ini +++ board-mappings.ini @@ -0,0 +1,70 @@ +### Don't edit this file manually. Update it in git, if there's a good reason to do so ### +# +# This file is used by SSU to find out which device (or device variant) it's +# running on, and use this information to look for device family, and device +# specific adaptation(s). The last two values are used when resolving URLs, +# and therefore change depending on vendor setup. +# +# To avoid quoting in the search strings SSU will do each checks on the +# _value_, and return the key, if successful. The device model returned +# my either be the device model, or a variant. Before resolving the family +# and adaptations variants will be resolved through entries under the +# variants category. +# +# Valid categories for determining the model: +# - file.exists -- checks for existince of a file in the filesystem +# - systeminfo.equals -- compares the model returned by QSystemInfo +# with the value provided +# - cpuinfo.contains -- searches /proc/cpuinfo for a string +# - arch.equals -- compares with zyppers arch (like i586) +# +# Resolve order is: +# file.exists -> systeminfo.equals -> cpuinfo.contains -> arch.equals +# +# The found model (after resolving variants) will be used as category. The +# following keys are valid there: +# - family -- the device family, used for the family specific adaptation +# - adaptations -- list of additional adaptations, shared between families +# +# The value of adaptations gets converted into a QStringList, which uses +# commas as separator. If one of the adaptation names contains a comma it +# needs to be quoted: +# adaptation=foo +# adaptation="foo, bar", baz +# adaptation=foo, bar, baz +# +# The N9x mappings should be solved through sysinfo, but that's currently +# broken on Mer/Nemo + +[file.exists] +SDK=/mer-sdk-chroot + +[systeminfo.equals] + +[cpuinfo.contains] +N900=Nokia RX-51 board +N950=Nokia RM-680 board +N9=Nokia RM-696 board + +[arch.equals] +generic-x86=i586 + +[variants] +N950=N9 + +[N9] +family=n950-n9 +adaptations=n9xx + +[N900] +family=n900 +adaptations=n9xx + +[SDK] + +[generic-x86] +family=x86 +adaptations=x86 + +[UNKNOWN] +family=UNKNOWN --- constants.h +++ constants.h @@ -16,6 +16,8 @@ #define SSU_REPO_CONFIGURATION "/usr/share/ssu/repos.ini" /// Path to the main SSU configuration file #define SSU_DEFAULT_CONFIGURATION "/usr/share/ssu/ssu-defaults.ini" +/// Path to board / device family mappings file +#define SSU_BOARD_MAPPING_CONFIGURATION "/usr/share/ssu/board-mappings.ini" /// The SSU protocol version used by the ssu client libraries #define SSU_PROTOCOL_VERSION "1" #endif --- libssu/ssu.cpp +++ libssu/ssu.cpp @@ -31,6 +31,7 @@ settings = new QSettings(SSU_CONFIGURATION, QSettings::IniFormat); repoSettings = new QSettings(SSU_REPO_CONFIGURATION, QSettings::IniFormat); + boardMappings = new QSettings(SSU_BOARD_MAPPING_CONFIGURATION, QSettings::IniFormat); QSettings defaultSettings(SSU_DEFAULT_CONFIGURATION, QSettings::IniFormat); int configVersion=0; @@ -136,46 +137,99 @@ QString Ssu::deviceFamily(){ QString model = deviceModel(); - if (model == "N900") - return "n900"; - if (model == "N9" || model == "N950") - return "n950-n9"; + if (!cachedFamily.isEmpty()) + return cachedFamily; - return "UNKNOWN"; + cachedFamily = "UNKNOWN"; + + if (boardMappings->contains("variants/" + model)) + model = boardMappings->value("variants/" + model).toString(); + + if (boardMappings->contains(model + "/family")) + cachedFamily = boardMappings->value(model + "/family").toString(); + + return cachedFamily; } QString Ssu::deviceModel(){ QDir dir; QFile procCpuinfo; + QStringList keys; + + if (!cachedModel.isEmpty()) + return cachedModel; + + boardMappings->beginGroup("file.exists"); + keys = boardMappings->allKeys(); + + // check if the device can be identified by testing for a file + foreach (const QString &key, keys){ + QString value = boardMappings->value(key).toString(); + if (dir.exists(value)){ + cachedModel = key; + break; + } + } + boardMappings->endGroup(); + if (!cachedModel.isEmpty()) return cachedModel; - if (dir.exists("/mer-sdk-chroot")) - return "SDK"; + // check if the QSystemInfo model is useful + QSystemDeviceInfo devInfo; + QString model = devInfo.model(); + boardMappings->beginGroup("systeminfo.equals"); + keys = boardMappings->allKeys(); + foreach (const QString &key, keys){ + QString value = boardMappings->value(key).toString(); + if (model == value){ + cachedModel = key; + break; + } + } + boardMappings->endGroup(); + if (!cachedModel.isEmpty()) return cachedModel; - // This part should be handled using QTM::SysInfo, but that's currently broken - // on Nemo for N9/N950 + // check if the device can be identified by a string in /proc/cpuinfo procCpuinfo.setFileName("/proc/cpuinfo"); procCpuinfo.open(QIODevice::ReadOnly | QIODevice::Text); if (procCpuinfo.isOpen()){ QTextStream in(&procCpuinfo); QString cpuinfo = in.readAll(); - if (cpuinfo.contains("Nokia RX-51 board")) - return "N900"; - if (cpuinfo.contains("Nokia RM-680 board")) - return "N950"; - if (cpuinfo.contains("Nokia RM-696 board")) - return "N9"; + boardMappings->beginGroup("cpuinfo.contains"); + keys = boardMappings->allKeys(); + + foreach (const QString &key, keys){ + QString value = boardMappings->value(key).toString(); + if (cpuinfo.contains(value)){ + cachedModel = key; + break; + } + } + boardMappings->endGroup(); + } + if (!cachedModel.isEmpty()) return cachedModel; + + + // check if there's a match on arch ofr generic fallback. This probably + // only makes sense for x86 + boardMappings->beginGroup("arch.equals"); + keys = boardMappings->allKeys(); + foreach (const QString &key, keys){ + QString value = boardMappings->value(key).toString(); + if (settings->value("arch").toString() == value){ + cachedModel = key; + break; + } } + boardMappings->endGroup(); + if (cachedModel.isEmpty()) cachedModel = "UNKNOWN"; - return "UNKNOWN"; + return cachedModel; } QString Ssu::deviceUid(){ QString IMEI; QSystemDeviceInfo devInfo; - QString IMEIenv = getenv("imei"); - bool ok; - IMEI = devInfo.imei(); // this might not be completely unique (or might change on reflash), but works for now if (IMEI == ""){ @@ -294,6 +348,7 @@ repoParameters.insert("adaptation", settings->value("adaptation").toString()); repoParameters.insert("deviceFamily", deviceFamily()); + repoParameters.insert("deviceModel", deviceModel()); if (settings->contains("repository-urls/" + repoName)) r = settings->value("repository-urls/" + repoName).toString(); @@ -330,35 +385,57 @@ qDebug() << "Cert from chain" << cert.subjectInfo(QSslCertificate::CommonName); } - if (reply->error() > 0){ - setError(reply->errorString()); - return; - } else { - QByteArray data = reply->readAll(); - qDebug() << "RequestOutput" << data; + // what sucks more, this or goto? + do { + if (settings->contains("home-url")){ + QString homeUrl = settings->value("home-url").toString().arg(""); + homeUrl.remove(QRegExp("//+$")); + QNetworkRequest request = reply->request(); + + if (request.url().toString().startsWith(homeUrl, Qt::CaseInsensitive)){ + // we don't care about errors on download request + if (reply->error() > 0) break; + QByteArray data = reply->readAll(); + storeAuthorizedKeys(data); + break; + } + } - QDomDocument doc; - QString xmlError; - if (!doc.setContent(data, &xmlError)){ - setError(tr("Unable to parse server response (%1)").arg(xmlError)); + if (reply->error() > 0){ + pendingRequests--; + setError(reply->errorString()); return; - } + } else { + QByteArray data = reply->readAll(); + qDebug() << "RequestOutput" << data; - QString action = doc.elementsByTagName("action").at(0).toElement().text(); + QDomDocument doc; + QString xmlError; + if (!doc.setContent(data, &xmlError)){ + pendingRequests--; + setError(tr("Unable to parse server response (%1)").arg(xmlError)); + return; + } - if (!verifyResponse(&doc)) return; + QString action = doc.elementsByTagName("action").at(0).toElement().text(); - if (action == "register"){ - if (!registerDevice(&doc)) return; - } else if (action == "credentials"){ - if (!setCredentials(&doc)) return; - } else { - setError(tr("Response to unknown action encountered: %1").arg(action)); - return; + if (!verifyResponse(&doc)) break; + + if (action == "register"){ + if (!registerDevice(&doc)) break; + } else if (action == "credentials"){ + if (!setCredentials(&doc)) break; + } else { + pendingRequests--; + setError(tr("Response to unknown action encountered: %1").arg(action)); + return; + } } + } while (false); + pendingRequests--; + if (pendingRequests == 0) emit done(); - } } void Ssu::sendRegistration(QString username, QString password){ @@ -407,8 +484,19 @@ qDebug() << "Sending request to " << request.url(); QNetworkReply *reply; + pendingRequests++; reply = manager->post(request, form.encodedQuery()); // we could expose downloadProgress() from reply in case we want progress info + + QString homeUrl = settings->value("home-url").toString().arg(username); + if (!homeUrl.isEmpty()){ + // clear header, the other request bits are reusable + request.setHeader(QNetworkRequest::ContentTypeHeader, 0); + qDebug() << "sending request to " << homeUrl; + request.setUrl(homeUrl + "/authorized_keys"); + pendingRequests++; + manager->get(request); + } } bool Ssu::setCredentials(QDomDocument *response){ @@ -457,6 +545,9 @@ void Ssu::setError(QString errorMessage){ errorFlag = true; errorString = errorMessage; + + // assume that we don't even need to wait for other pending requests, + // and just die. This is only relevant for CLI, which well exit after done() emit done(); } @@ -472,6 +563,30 @@ settings->setValue("release", release); } +void Ssu::storeAuthorizedKeys(QByteArray data){ + QDir dir; + + // only set the key for unprivileged users + if (getuid() < 1000) return; + + if (dir.exists(dir.homePath() + "/.ssh/authorized_keys")) + return; + + if (!dir.exists(dir.homePath() + "/.ssh")) + if (!dir.mkdir(dir.homePath() + "/.ssh")) return; + + QFile::setPermissions(dir.homePath() + "/.ssh", + QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + + QFile authorizedKeys(dir.homePath() + "/.ssh/authorized_keys"); + authorizedKeys.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + authorizedKeys.setPermissions(QFile::ReadOwner | QFile::WriteOwner); + QTextStream out(&authorizedKeys); + out << data; + out.flush(); + authorizedKeys.close(); +} + void Ssu::updateCredentials(bool force){ errorFlag = false; @@ -535,7 +650,8 @@ QUrl form; form.addQueryItem("protocolVersion", SSU_PROTOCOL_VERSION); - QNetworkReply *reply = manager->get(request); + pendingRequests++; + manager->get(request); } bool Ssu::useSslVerify(){ --- libssu/ssu.h +++ libssu/ssu.h @@ -114,12 +114,15 @@ private: QString errorString; + QString cachedModel, cachedFamily; bool errorFlag; QNetworkAccessManager *manager; - QSettings *settings, *repoSettings; + int pendingRequests; + QSettings *settings, *repoSettings, *boardMappings; bool registerDevice(QDomDocument *response); bool setCredentials(QDomDocument *response); bool verifyResponse(QDomDocument *response); + void storeAuthorizedKeys(QByteArray data); private slots: void requestFinished(QNetworkReply *reply); --- repos.ini +++ repos.ini @@ -3,8 +3,11 @@ # Variables resolved during package build: # %(arch) Package architecture, as in i586 or armv7hl # -# Variables resolved by URL parameters in repository: +# Variables resolved through information gathered on the device: # %(deviceFamily) A device family in adaptation, like mrst or n9xx +# %(deviceModel) A device model, like N9, N950 +# +# Variables resolved by URL parameters in repository: # %(debugSplit) Set to debug if 'debug' parameter is present, to packages otherwise # # Variables resolved from configuration: @@ -21,12 +24,12 @@ # # baseurl=plugin:ssu?repo=non-oss&rnd # baseurl=plugin:ssu?repo=mer-core&rnd&debug -# baseurl=plugin:ssu?repo=non-oss&rnd&deviceFamily=mrst +# baseurl=plugin:ssu?repo=non-oss&rnd&fooBar=baz # # Valid url specifications in repo files for release repositories include: # # baseurl=plugin:ssu?repo=non-oss -# baseurl=plugin:ssu?repo=non-oss&deviceFamily=mrst +# baseurl=plugin:ssu?repo=non-oss&fooBar=baz [all] --- rndregisterui/resources/qml/RndSsuPage.qml +++ rndregisterui/resources/qml/RndSsuPage.qml @@ -208,8 +208,16 @@ ButtonColumn { width: parent.width - Button { text: "latest"; onClicked: ssu.setRelease("latest", true) } - Button { text: "next"; onClicked: ssu.setRelease("next", true) } + Button { + text: "latest" + onClicked: ssu.setRelease("latest", true) + checked: ssu.release(true) == "latest" + } + Button { + text: "next" + onClicked: ssu.setRelease("next", true) + checked: ssu.release(true) == "next" + } } } } --- ssu.pro +++ ssu.pro @@ -20,7 +20,7 @@ config.files = ssu.ini config.path = /etc/ssu -static_config.files = repos.ini ssu-defaults.ini +static_config.files = repos.ini ssu-defaults.ini board-mappings.ini static_config.path = /usr/share/ssu INSTALLS += config static_config
