[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: