Bug#545139: [PATCH] akonadi-server: not possible to create socket in AFS $HOME
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 <ansgar@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;
Reply to: