tags 545139 + patch thanks The attached patch changes Akonadi to create a symlink .../socket-$HOSTNAME to a directory in /tmp, and to use this directory for sockets. This is similar to how KDE handles sockets in $KDEHOME/socket-$HOSTNAME.
There is at least one known issue with the patch: it overwrites the Options setting as the path in UNIX_SOCKET depends on the hostname. With the patch applied, Akonadi can start with $HOME on AFS here and KMail seems to be working fine as well (cf. #604805). It would be nice if this patch could be included in Squeeze. Regards, Ansgar
From: Ansgar Burchardt <ans...@mathi.uni-heidelberg.de> Date: Mon, 29 Nov 2010 12:40:18 +0100 Subject: Move sockets away from $HOME Bug-Debian: http://bugs.debian.org/545139 Bug: https://bugs.kde.org/show_bug.cgi?id=179006 Move directories used for sockets to /tmp, using a symlink (that includes the hostname) to remember where it is located. Known issues: - User changes to Options are overwritten as we need to set UNIX_SOCKET=... --- akonadi-1.3.1.orig/server/CMakeLists.txt +++ akonadi-1.3.1/server/CMakeLists.txt @@ -59,6 +59,7 @@ set(libakonadiprivate_SRCS ${AKONADI_SHARED_SOURCES} src/akonadi.cpp + src/socketdir.cpp src/akonadiconnection.cpp src/handler.cpp src/handlerhelper.cpp --- akonadi-1.3.1.orig/server/src/akonadi.cpp +++ akonadi-1.3.1/server/src/akonadi.cpp @@ -34,6 +34,7 @@ #include "debuginterface.h" #include "storage/itemretrievalthread.h" #include "preprocessormanager.h" +#include "socketdir.h" #include "libs/xdgbasedirs_p.h" #include "libs/protocol_p.h" @@ -84,8 +85,9 @@ connectionSettings.setValue( QLatin1String( "Data/Method" ), QLatin1String( "NamedPipe" ) ); connectionSettings.setValue( QLatin1String( "Data/NamedPipe" ), namedPipe ); #else - const QString defaultSocketDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ); - QString socketDir = settings.value( QLatin1String( "Connection/SocketDirectory" ), defaultSocketDir ).toString(); + QString socketDir = settings.value( QLatin1String( "Connection/SocketDirectory" ), QString() ).toString(); + if (socketDir.isEmpty()) + socketDir = akonadiSocketDirectory(); if ( socketDir[0] != QLatin1Char( '/' ) ) { QDir::home().mkdir( socketDir ); socketDir = QDir::homePath() + QLatin1Char( '/' ) + socketDir; @@ -202,8 +204,9 @@ #ifndef Q_OS_WIN QSettings connectionSettings( connectionSettingsFile, QSettings::IniFormat ); - const QString defaultSocketDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ); - const QString socketDir = settings.value( QLatin1String( "Connection/SocketDirectory" ), defaultSocketDir ).toString(); + QString socketDir = settings.value( QLatin1String( "Connection/SocketDirectory" ), QString() ).toString(); + if (socketDir.isEmpty()) + socketDir = akonadiSocketDirectory(); if ( !QDir::home().remove( socketDir + QLatin1String( "/akonadiserver.socket" ) ) ) akError() << "Failed to remove Unix socket"; @@ -261,7 +264,7 @@ void AkonadiServer::startPostgresqlDatabaseProcess() { const QString dataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_data" ) ); - const QString socketDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_misc" ) ); + const QString socketDir = akonadiSocketDirectory(); if ( !QFile::exists( QString::fromLatin1( "%1/PG_VERSION" ).arg( dataDir ) ) ) { // postgre data directory not initialized yet, so call initdb on it @@ -371,7 +374,7 @@ const QString dataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_data" ) ); const QString akDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/" ) ); - const QString miscDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_misc" ) ); + const QString miscDir = akonadiSocketDirectory(); const QString fileDataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/file_db_data" ) ); // generate config file --- /dev/null +++ akonadi-1.3.1/server/src/socketdir.cpp @@ -0,0 +1,106 @@ +#include "socketdir.h" + +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include "libs/xdgbasedirs_p.h" +#include <pwd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <QDebug> + +static bool check_socket_directory(const char *path); +static bool create_socket_directory(const char *link, const char *tmpl); + +QString Akonadi::akonadiSocketDirectory() +{ + char hostname[4096]; + struct passwd *pw_ent; + uid_t uid = getuid(); + + if (gethostname(hostname, sizeof(hostname)) == -1) { + qCritical() << "gethostname() failed:" << strerror(errno); + return QString(); + } + + pw_ent = getpwuid(uid); + if (!pw_ent) { + qCritical() << "Could not get passwd entry for user id" << uid; + return QString(); + } + + QString link = XdgBaseDirs::saveDir("data", QLatin1String("akonadi")) + QLatin1Char('/') + QLatin1String("socket-") + QLatin1String(hostname); + QString tmpl = QLatin1String("akonadi-") + QLatin1String(pw_ent->pw_name) + QLatin1String(".XXXXXX"); + + if (check_socket_directory(link.toUtf8().constData())) + return link; + + if (create_socket_directory(link.toUtf8().constData(), tmpl.toUtf8().constData())) + return link; + + qCritical() << "Could not create socket directory for Akonadi."; + return QString(); +} + +static bool check_socket_directory(const char *path) +{ + struct stat st; +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + char realpath[PATH_MAX]; + + if (lstat(path, &st) == -1) + return false; + + if (S_ISLNK(st.st_mode)) { + /* follow only a single symlink */ + if (readlink(path, realpath, sizeof(realpath)) == -1) + return false; + if (lstat(realpath, &st) == -1) + return false; + } + + if (!S_ISDIR(st.st_mode)) + return false; + + if (st.st_uid != getuid()) + return false; + + return true; +} + +static bool create_socket_directory(const char *link, const char *tmpl) +{ + char *tmp = getenv("TMPDIR"); + if (!tmp || !tmp[0]) + tmp = "/tmp"; + + char *directory = static_cast<char*>(malloc(strlen(tmp) + 1 + strlen(tmpl) + 1)); + strcpy(directory, tmp); + strcat(directory, "/"); + strcat(directory, tmpl); + + if (!mkdtemp(directory)) { + qCritical() << "Creating socket directory with template" << directory << "failed:" << strerror(errno); + goto failure; + } + + if (unlink(link) == -1 && errno != ENOENT) { + qCritical() << "Removing symlink" << link << "failed:" << strerror(errno); + goto failure; + } + + if (symlink(directory, link) == -1) { + qCritical() << "Creating symlink from" << directory << "to" << link << "failed:" << strerror(errno); + goto failure; + } + + free(directory); + return true; + +failure: + free(directory); + return false; +} --- /dev/null +++ akonadi-1.3.1/server/src/socketdir.h @@ -0,0 +1,10 @@ +#ifndef AKONADI_SOCKETDIR_H +#define AKONADI_SOCKETDIR_H + +#include <QtCore/QString> + +namespace Akonadi { + QString akonadiSocketDirectory(); +} + +#endif --- akonadi-1.3.1.orig/server/src/storage/dbconfig.cpp +++ akonadi-1.3.1/server/src/storage/dbconfig.cpp @@ -20,6 +20,7 @@ #include "dbconfig.h" #include "akdebug.h" #include "../../libs/xdgbasedirs_p.h" +#include "../socketdir.h" #include <QDir> #include <QFile> @@ -101,7 +102,7 @@ } mInternalServer = settings.value( QLatin1String("QMYSQL/StartServer"), defaultInternalServer ).toBool(); if ( mInternalServer ) { - const QString miscDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_misc" ) ); + const QString miscDir = akonadiSocketDirectory(); defaultOptions = QString::fromLatin1( "UNIX_SOCKET=%1/mysql.socket" ).arg( miscDir ); } } else if ( mDriverName == QLatin1String("QMYSQL_EMBEDDED") ) { @@ -142,7 +143,7 @@ // verify settings and apply permanent changes (written out below) if ( mDriverName == QLatin1String( "QMYSQL" ) ) { - if ( mInternalServer && mConnectionOptions.isEmpty() ) + if ( mInternalServer /* && mConnectionOptions.isEmpty() */ ) mConnectionOptions = defaultOptions; if ( mInternalServer && (mServerPath.isEmpty() || !QFile::exists(mServerPath) ) ) mServerPath = defaultServerPath;