diff --git a/NEWS b/NEWS
index 461cc85a..df015fa5 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,13 @@ Changes in 3.0.3
* FIX: Fix an issue with user homes when user home directory has not the
same name as the username.
Fixes bug #497.
+* UPD: Fix PAM config install, new default installation dir is
+ $sysconfdir/pam.d/. Add configure option --with-pam-confdir
+ to specify alternative path.
+* NEW: AFP stats about active session via dbus IPC. Client side python
+ program `afpstats`. Requires dbus, dbus-glib any python-dbus.
+ configure option --dbus-sysconf-dir for specifying dbus
+ system security configuration files.
Changes in 3.0.2
================
diff --git a/config/Makefile.am b/config/Makefile.am
index 57c85ff3..e72c38f3 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -6,13 +6,19 @@ SUFFIXES = .tmpl .
TMPLFILES = afp.conf.tmpl
GENFILES = afp.conf
CLEANFILES = $(GENFILES)
-EXTRA_DIST = afp.conf.tmpl extmap.conf
+EXTRA_DIST = afp.conf.tmpl extmap.conf netatalk-dbus.conf
OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
CONFFILES = extmap.conf
pkgconfdir = @PKGCONFDIR@
+
+if HAVE_DBUS_GLIB
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = netatalk-dbus.conf
+endif
+
#
# rule to parse template files
#
diff --git a/config/netatalk-dbus.conf b/config/netatalk-dbus.conf
new file mode 100644
index 00000000..1646efd4
--- /dev/null
+++ b/config/netatalk-dbus.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/pam/.gitignore b/config/pam/.gitignore
index dbbab4b2..d209ccf9 100644
--- a/config/pam/.gitignore
+++ b/config/pam/.gitignore
@@ -1,5 +1,5 @@
Makefile
Makefile.in
netatalk.pam
-.gitignore
+netatalk
*.o
diff --git a/config/pam/Makefile.am b/config/pam/Makefile.am
index e01d784c..62affbda 100644
--- a/config/pam/Makefile.am
+++ b/config/pam/Makefile.am
@@ -1,10 +1,9 @@
## Makefile for distrib/pam/
SUFFIXES = .tmpl .
-pamdir = @PAMDIR@/etc/pam.d
-EXTRA_DIST = netatalk.pam.tmpl
-noinst_SCRIPTS = netatalk.pam
-CLEANFILES = netatalk.pam
+EXTRA_DIST = netatalk.tmpl
+noinst_SCRIPTS = netatalk
+CLEANFILES = netatalk
.tmpl:
sed -e "s,[@]PAM_DIRECTIVE[@],${PAM_DIRECTIVE},g" \
@@ -15,23 +14,6 @@ CLEANFILES = netatalk.pam
<$< >$@
if USE_PAM
-install-data-local: netatalk.pam
- $(mkinstalldirs) $(DESTDIR)$(pamdir)
- if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f $(DESTDIR)$(pamdir)/netatalk; then \
- echo "$(INSTALL_DATA) $$f $(DESTDIR)$(pamdir)/netatalk"; \
- $(INSTALL_DATA) netatalk.pam $(DESTDIR)$(pamdir)/netatalk || echo "WARNING: Can't install PAM files"; \
- else \
- echo "not overwriting $(DESTDIR)$(pamdir)/netatalk"; \
- fi;
-
-uninstall-local:
- echo rm -f $(DESTDIR)$(pamdir)/netatalk; \
- rm -f $(DESTDIR)$(pamdir)/netatalk; \
- for f in $(CONFFILES) $(GENFILES); do \
- echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
- rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
- done
-else
-install-data-local:
-uninstall-local:
+pamdir = $(PAMDIR)
+pam_DATA = netatalk
endif
diff --git a/config/pam/netatalk.pam.tmpl b/config/pam/netatalk.tmpl
similarity index 100%
rename from config/pam/netatalk.pam.tmpl
rename to config/pam/netatalk.tmpl
diff --git a/configure.ac b/configure.ac
index 3c8843d1..88f81aaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -189,6 +189,9 @@ AC_NETATALK_FHS
dnl netatalk lockfile path, must come after AC_NETATALK_FHS
AC_NETATALK_LOCKFILE
+dnl Check for dbus-glib, for AFP stats on dbus
+AC_NETATALK_DBUS_GLIB
+
CFLAGS="-I\$(top_srcdir)/include -I\$(top_srcdir)/sys $CFLAGS"
UAMS_PATH="${uams_path}"
diff --git a/contrib/shell_utils/Makefile.am b/contrib/shell_utils/Makefile.am
index bb4d9cbf..dc4c7db7 100644
--- a/contrib/shell_utils/Makefile.am
+++ b/contrib/shell_utils/Makefile.am
@@ -18,6 +18,6 @@ SUFFIXES = .tmpl .
CLEANFILES = $(GENERATED_FILES)
-bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
+bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES) afpstats
-EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl
+EXTRA_DIST = $(TEMPLATE_FILES) make-casetable.pl make-precompose.h.pl afpstats
diff --git a/contrib/shell_utils/afpstats b/contrib/shell_utils/afpstats
new file mode 100755
index 00000000..8c5413c8
--- /dev/null
+++ b/contrib/shell_utils/afpstats
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python afpstats.py
+"""
+
+import sys
+from traceback import print_exc
+import dbus
+
+def main():
+ bus = dbus.SystemBus()
+
+ try:
+ remote_object = bus.get_object("org.netatalk.AFPStats",
+ "/org/netatalk/AFPStats")
+
+ except dbus.DBusException:
+ print_exc()
+ sys.exit(1)
+
+ iface = dbus.Interface(remote_object, "org.netatalk.AFPStats")
+
+ reply = iface.GetUsers()
+ for name in reply:
+ print name
+
+if __name__ == '__main__':
+ main()
diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am
index 2404ad7d..1612344a 100644
--- a/etc/afpd/Makefile.am
+++ b/etc/afpd/Makefile.am
@@ -1,6 +1,10 @@
# Makefile.am for etc/afpd/
pkgconfdir = @PKGCONFDIR@
+BUILT_SOURCES =
+EXTRA_DIST =
+CLEANFILES =
+DISTCLEANFILES =
sbin_PROGRAMS = afpd
noinst_PROGRAMS = hash fce
@@ -61,11 +65,28 @@ if HAVE_ACLS
afpd_SOURCES += acls.c
endif
+if HAVE_DBUS_GLIB
+BUILT_SOURCES += afpstats_service_glue.h
+EXTRA_DIST += afpstats-service.xml
+DISTCLEANFILES += afpstats_service_glue.h
+
+afpstats_service_glue.h: afpstats-service.xml
+ $(LIBTOOL) --mode=execute \
+ dbus-binding-tool \
+ --prefix=afpstats_obj \
+ --mode=glib-server \
+ --output=afpstats_service_glue.h \
+ $(top_srcdir)/etc/afpd/afpstats-service.xml
+
+afpd_SOURCES += afpstats.c afpstats_obj.c
+afpd_CFLAGS += $(DBUS_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION
+afpd_LDFLAGS += $(DBUS_LIBS) $(DBUS_GLIB_LIBS) -ldbus-glib-1
+endif
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
- dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h
+ dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h afpstats.h afpstats_obj.h
hash_SOURCES = hash.c
hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c
index 77918c56..646e8d12 100644
--- a/etc/afpd/afp_dsi.c
+++ b/etc/afpd/afp_dsi.c
@@ -473,6 +473,8 @@ void afp_over_dsi(AFPObj *obj)
int flag = 1;
setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
+ ipc_child_state(obj, DSI_RUNNING);
+
/* get stuck here until the end */
while (1) {
if (sigsetjmp(recon_jmp, 1) != 0)
@@ -497,15 +499,6 @@ void afp_over_dsi(AFPObj *obj)
exit(0);
}
-#if 0
- /* got ECONNRESET in read from client => exit*/
- if (dsi->flags & DSI_GOT_ECONNRESET) {
- LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
- afp_dsi_close(obj);
- exit(0);
- }
-#endif
-
if (dsi->flags & DSI_RECONINPROG) {
LOG(log_note, logtype_afpd, "afp_over_dsi: failed reconnect");
afp_dsi_close(obj);
@@ -516,8 +509,11 @@ void afp_over_dsi(AFPObj *obj)
if (dsi_disconnect(dsi) != 0)
afp_dsi_die(EXITERR_CLNT);
+ ipc_child_state(obj, DSI_DISCONNECTED);
+
while (dsi->flags & DSI_DISCONNECTED)
pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+ ipc_child_state(obj, DSI_RUNNING);
continue; /* continue receiving until disconnect timer expires
* or a primary reconnect succeeds */
}
@@ -526,6 +522,7 @@ void afp_over_dsi(AFPObj *obj)
LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
dsi->flags &= ~DSI_SLEEPING;
dsi->tickle = 0;
+ ipc_child_state(obj, DSI_RUNNING);
}
if (reload_request) {
diff --git a/etc/afpd/afpstats-service.xml b/etc/afpd/afpstats-service.xml
new file mode 100644
index 00000000..099c778a
--- /dev/null
+++ b/etc/afpd/afpstats-service.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/etc/afpd/afpstats.c b/etc/afpd/afpstats.c
new file mode 100644
index 00000000..1dbacdf8
--- /dev/null
+++ b/etc/afpd/afpstats.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013 Frank Lahm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "afpstats_obj.h"
+#include "afpstats_service_glue.h"
+
+/*
+ * Beware: this struct is accessed and modified from the main thread
+ * and from this thread, thus be careful to lock and unlock the mutex.
+ */
+static server_child_t *childs;
+
+static gpointer afpstats_thread(gpointer _data)
+{
+ DBusGConnection *bus;
+ DBusGProxy *bus_proxy;
+ GError *error = NULL;
+ GMainLoop *thread_loop;
+ guint request_name_result;
+ sigset_t sigs;
+
+ /* Block all signals in this thread */
+ sigfillset(&sigs);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+
+ dbus_g_object_type_install_info(AFPSTATS_TYPE_OBJECT, &dbus_glib_afpstats_obj_object_info);
+
+ thread_loop = g_main_loop_new(NULL, FALSE);
+
+ if (!(bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error))) {
+ LOG(log_error, logtype_afpd,"Couldn't connect to system bus: %s", error->message);
+ return NULL;
+ }
+
+ if (!(bus_proxy = dbus_g_proxy_new_for_name(bus, "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus"))) {
+ LOG(log_error, logtype_afpd,"Couldn't create bus proxy");
+ return NULL;
+ }
+
+ if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error,
+ G_TYPE_STRING, "org.netatalk.AFPStats",
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &request_name_result,
+ G_TYPE_INVALID)) {
+ LOG(log_error, logtype_afpd, "Failed to acquire DBUS name: %s", error->message);
+ return NULL;
+ }
+
+ AFPStatsObj *obj = g_object_new(AFPSTATS_TYPE_OBJECT, NULL);
+ dbus_g_connection_register_g_object(bus, "/org/netatalk/AFPStats", G_OBJECT(obj));
+
+ g_main_loop_run(thread_loop);
+ return thread_loop;
+}
+
+static void my_glib_log(const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ LOG(log_error, logtype_afpd, "%s: %s", log_domain, message);
+}
+
+server_child_t *afpstats_get_and_lock_childs(void)
+{
+ pthread_mutex_lock(&childs->servch_lock);
+ return childs;
+}
+
+void afpstats_unlock_childs(void)
+{
+ pthread_mutex_unlock(&childs->servch_lock);
+}
+
+int afpstats_init(server_child_t *childs_in)
+{
+ GThread *thread;
+
+ childs = childs_in;
+ g_type_init();
+ (void)g_log_set_default_handler(my_glib_log, NULL);
+ g_thread_init(NULL);
+ thread = g_thread_create(afpstats_thread, NULL, TRUE, NULL);
+
+ return 0;
+}
diff --git a/etc/afpd/afpstats.h b/etc/afpd/afpstats.h
new file mode 100644
index 00000000..9f742ad7
--- /dev/null
+++ b/etc/afpd/afpstats.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Frank Lahm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AFPD_AFPSTATS_H
+#define AFPD_AFPSTATS_H
+
+#include
+
+extern int afpstats_init(server_child_t *);
+extern server_child_t *afpstats_get_and_lock_childs(void);
+extern void afpstats_unlock_childs(void);
+#endif
diff --git a/etc/afpd/afpstats_obj.c b/etc/afpd/afpstats_obj.c
new file mode 100644
index 00000000..755d7a59
--- /dev/null
+++ b/etc/afpd/afpstats_obj.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Frank Lahm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "afpstats.h"
+#include "afpstats_obj.h"
+
+struct AFPStatsObj
+{
+ GObject parent;
+};
+
+struct AFPStatsObjClass
+{
+ GObjectClass parent;
+};
+
+static void afpstats_obj_init(AFPStatsObj *obj)
+{
+}
+
+static void afpstats_obj_class_init(AFPStatsObjClass *klass)
+{
+}
+
+static gpointer afpstats_obj_parent_class = NULL;
+
+static void afpstats_obj_class_intern_init(gpointer klass)
+{
+ afpstats_obj_parent_class = g_type_class_peek_parent(klass);
+ afpstats_obj_class_init((AFPStatsObjClass *)klass);
+}
+
+GType afpstats_obj_get_type(void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter(&g_define_type_id__volatile)) {
+ GType g_define_type_id = g_type_register_static_simple(
+ G_TYPE_OBJECT,
+ g_intern_static_string("AFPStatsObj"),
+ sizeof(AFPStatsObjClass),
+ (GClassInitFunc)afpstats_obj_class_intern_init,
+ sizeof(AFPStatsObj),
+ (GInstanceInitFunc)afpstats_obj_init,
+ (GTypeFlags)0);
+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error)
+{
+ gchar **names;
+ server_child_t *childs = afpstats_get_and_lock_childs();
+ afp_child_t *child;
+ struct passwd *pw;
+ int i = 0, j;
+ char buf[256];
+
+ names = g_new(char *, childs->servch_count + 1);
+
+ for (j = 0; j < CHILD_HASHSIZE && i < childs->servch_count; j++) {
+ child = childs->servch_table[j];
+ while (child) {
+ if (child->afpch_valid && (pw = getpwuid(child->afpch_uid))) {
+ time_t time = child->afpch_logintime;
+ strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime(&time));
+ names[i++] = g_strdup_printf("name: %s, pid: %d, logintime: %s, state: %s, volumes: %s",
+ pw->pw_name, child->afpch_pid, buf,
+ child->afpch_state == DSI_RUNNING ? "active" :
+ child->afpch_state == DSI_SLEEPING ? "sleeping" :
+ child->afpch_state == DSI_EXTSLEEP ? "sleeping" :
+ child->afpch_state == DSI_DISCONNECTED ? "disconnected" :
+ "unknown",
+ child->afpch_volumes ? child->afpch_volumes : "-");
+ }
+ child = child->afpch_next;
+ }
+ }
+ names[i] = NULL;
+ *ret = names;
+
+ afpstats_unlock_childs();
+
+ return TRUE;
+}
diff --git a/etc/afpd/afpstats_obj.h b/etc/afpd/afpstats_obj.h
new file mode 100644
index 00000000..9d830006
--- /dev/null
+++ b/etc/afpd/afpstats_obj.h
@@ -0,0 +1,19 @@
+#ifndef AFPSTATS_OBJ_H
+#define AFPSTATS_OBJ_H
+
+#include
+
+typedef struct AFPStatsObj AFPStatsObj;
+typedef struct AFPStatsObjClass AFPStatsObjClass;
+
+GType afpstats_obj_get_type(void);
+gboolean afpstats_obj_get_users(AFPStatsObj *obj, gchar ***ret, GError **error);
+
+#define AFPSTATS_TYPE_OBJECT (afpstats_obj_get_type ())
+#define AFPSTATS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST((object), AFPSTATS_TYPE_OBJECT, AFPStatsObj))
+#define AFPSTATS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+#define AFPSTATS_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), AFPSTATS_TYPE_OBJECT))
+#define AFPSTATS_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), AFPSTATS_TYPE_OBJECT, AFPStatsObjClass))
+
+#endif /* AFPSTATS_OBJ_H */
diff --git a/etc/afpd/afpstats_service_glue.h b/etc/afpd/afpstats_service_glue.h
new file mode 100644
index 00000000..65081c46
--- /dev/null
+++ b/etc/afpd/afpstats_service_glue.h
@@ -0,0 +1,121 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+#define __dbus_glib_marshal_afpstats_obj_MARSHAL_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER */
+extern void dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+void
+dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_afpstats_obj_MARSHAL_H__ */
+
+#include
+static const DBusGMethodInfo dbus_glib_afpstats_obj_methods[] = {
+ { (GCallback) afpstats_obj_get_users, dbus_glib_marshal_afpstats_obj_BOOLEAN__POINTER_POINTER, 0 },
+};
+
+const DBusGObjectInfo dbus_glib_afpstats_obj_object_info = { 1,
+ dbus_glib_afpstats_obj_methods,
+ 1,
+"org.netatalk.AFPStats\0GetUsers\0S\0ret\0O\0F\0N\0as\0\0\0",
+"\0",
+"\0"
+};
+
diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c
index 5246d436..fa60b192 100644
--- a/etc/afpd/auth.c
+++ b/etc/afpd/auth.c
@@ -375,6 +375,7 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
if (dsi->flags & DSI_EXTSLEEP) {
LOG(log_note, logtype_afpd, "afp_zzz: waking up from extended sleep");
dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
+ ipc_child_state(obj, DSI_RUNNING);
}
} else {
/* sleep request */
@@ -382,8 +383,10 @@ int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen
if (data & AFPZZZ_EXT_SLEEP) {
LOG(log_note, logtype_afpd, "afp_zzz: entering extended sleep");
dsi->flags |= DSI_EXTSLEEP;
+ ipc_child_state(obj, DSI_EXTSLEEP);
} else {
LOG(log_note, logtype_afpd, "afp_zzz: entering normal sleep");
+ ipc_child_state(obj, DSI_SLEEPING);
}
}
diff --git a/etc/afpd/main.c b/etc/afpd/main.c
index a66e4a86..22650479 100644
--- a/etc/afpd/main.c
+++ b/etc/afpd/main.c
@@ -38,6 +38,7 @@
#include "fork.h"
#include "uam_auth.h"
#include "afp_zeroconf.h"
+#include "afpstats.h"
#define AFP_LISTENERS 32
#define FDSET_SAFETY 5
@@ -326,6 +327,11 @@ int main(int ac, char **av)
/* set limits */
(void)setlimits();
+#ifdef HAVE_DBUS_GLIB
+ /* Run dbus AFP statics thread */
+ (void)afpstats_init(server_children);
+#endif
+
afp_child_t *child;
int recon_ipc_fd;
pid_t pid;
diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c
index 9c7eb2bd..f5122373 100644
--- a/etc/afpd/volume.c
+++ b/etc/afpd/volume.c
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#ifdef CNID_DB
#include
@@ -659,6 +660,31 @@ static int volume_openDB(const AFPObj *obj, struct vol *volume)
return (!volume->v_cdb)?-1:0;
}
+/*
+ * Send list of open volumes to afpd master via IPC
+ */
+static void server_ipc_volumes(AFPObj *obj)
+{
+ struct vol *volume, *vols;
+ volume = vols = getvolumes();
+ bstring openvolnames = bfromcstr("");
+ bool firstvol = true;
+
+ while (volume) {
+ if (volume->v_flags & AFPVOL_OPEN) {
+ if (!firstvol)
+ bcatcstr(openvolnames, ", ");
+ else
+ firstvol = false;
+ bcatcstr(openvolnames, volume->v_localname);
+ }
+ volume = volume->v_next;
+ }
+
+ ipc_child_write(obj->ipc_fd, IPC_VOLUMES, blength(openvolnames), bdata(openvolnames));
+ bdestroy(openvolnames);
+}
+
/* -------------------------
* we are the user here
*/
@@ -834,6 +860,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
setmessage(msg);
free(vol_mname);
+ server_ipc_volumes(obj);
return( AFP_OK );
}
@@ -906,6 +933,7 @@ int afp_closevol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
(void)chdir("/");
curdir = NULL;
closevol(obj, vol);
+ server_ipc_volumes(obj);
return( AFP_OK );
}
diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h
index 4197dba5..fc1af468 100644
--- a/include/atalk/dsi.h
+++ b/include/atalk/dsi.h
@@ -154,9 +154,6 @@ typedef struct DSI {
#define DSI_RECONSOCKET (1 << 7) /* we have a new socket from primary reconnect */
#define DSI_RECONINPROG (1 << 8) /* used in the new session in reconnect */
#define DSI_AFP_LOGGED_OUT (1 << 9) /* client called afp_logout, quit on next EOF from socket */
-#if 0
-#define DSI_GOT_ECONNRESET (1 << 10) /* got ECONNRESET from client => exit */
-#endif
/* basic initialization: dsi_init.c */
extern DSI *dsi_init(AFPObj *obj, const char *hostname, const char *address, const char *port);
diff --git a/include/atalk/server_child.h b/include/atalk/server_child.h
index 6f8f18a0..5e694018 100644
--- a/include/atalk/server_child.h
+++ b/include/atalk/server_child.h
@@ -20,10 +20,13 @@ typedef struct afp_child {
uid_t afpch_uid; /* user id of connected client (from the worker afpd process) */
int afpch_valid; /* 1 if we have a clientid */
int afpch_killed; /* 1 if we already tried to kill the client */
- uint32_t afpch_time; /* client boot time (from the mac client) */
+ uint32_t afpch_boottime; /* client boot time (from the mac client) */
+ time_t afpch_logintime; /* time the child was added */
uint32_t afpch_idlen; /* clientid len (from the Mac client) */
char *afpch_clientid; /* clientid (from the Mac client) */
int afpch_ipc_fd; /* socket for IPC bw afpd parent and childs */
+ int16_t afpch_state; /* state of AFP session (eg active, sleeping, disconnected) */
+ char *afpch_volumes; /* mounted volumes */
struct afp_child **afpch_prevp;
struct afp_child *afpch_next;
} afp_child_t;
@@ -34,7 +37,6 @@ typedef struct {
int servch_count; /* Current count of active AFP sessions */
int servch_nsessions; /* Number of allowed AFP sessions */
afp_child_t *servch_table[CHILD_HASHSIZE]; /* Hashtable with data of AFP sesssions */
- void (*servch_cleanup)(const pid_t); /* Cleanup handler */
} server_child_t;
/* server_child.c */
@@ -42,6 +44,7 @@ extern server_child_t *server_child_alloc(int);
extern afp_child_t *server_child_add(server_child_t *, pid_t, int ipc_fd);
extern int server_child_remove(server_child_t *, pid_t);
extern void server_child_free(server_child_t *);
+extern afp_child_t *server_child_resolve(server_child_t *childs, id_t pid);
extern void server_child_kill(server_child_t *, int);
extern void server_child_kill_one_by_id(server_child_t *children, pid_t pid, uid_t,
diff --git a/include/atalk/server_ipc.h b/include/atalk/server_ipc.h
index 0765c73c..50eb7edc 100644
--- a/include/atalk/server_ipc.h
+++ b/include/atalk/server_ipc.h
@@ -6,8 +6,11 @@
#define IPC_DISCOLDSESSION 0
#define IPC_GETSESSION 1
+#define IPC_STATE 2 /* pass AFP session state */
+#define IPC_VOLUMES 3 /* pass list of open volumes */
extern int ipc_server_read(server_child_t *children, int fd);
extern int ipc_child_write(int fd, uint16_t command, int len, void *token);
+extern int ipc_child_state(AFPObj *obj, uint16_t state);
#endif /* IPC_GETSESSION_LOGIN */
diff --git a/libatalk/dsi/dsi_stream.c b/libatalk/dsi/dsi_stream.c
index 711a037b..6c502b0b 100644
--- a/libatalk/dsi/dsi_stream.c
+++ b/libatalk/dsi/dsi_stream.c
@@ -471,10 +471,6 @@ size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
stored += len;
} else { /* eof or error */
/* don't log EOF error if it's just after connect (OSX 10.3 probe) */
-#if 0
- if (errno == ECONNRESET)
- dsi->flags |= DSI_GOT_ECONNRESET;
-#endif
if (len || stored || dsi->read_count) {
if (! (dsi->flags & DSI_DISCONNECTED)) {
LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
diff --git a/libatalk/util/server_child.c b/libatalk/util/server_child.c
index 6c86f483..da78c6a0 100644
--- a/libatalk/util/server_child.c
+++ b/libatalk/util/server_child.c
@@ -70,11 +70,11 @@ static inline void unhash_child(afp_child_t *child)
}
}
-static afp_child_t *resolve_child(afp_child_t **table, pid_t pid)
+afp_child_t *server_child_resolve(server_child_t *childs, id_t pid)
{
afp_child_t *child;
- for (child = table[HASH(pid)]; child; child = child->afpch_next) {
+ for (child = childs->servch_table[HASH(pid)]; child; child = child->afpch_next) {
if (child->afpch_pid == pid)
break;
}
@@ -113,7 +113,7 @@ afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
}
/* if we already have an entry. just return. */
- if ((child = resolve_child(children->servch_table, pid)))
+ if ((child = server_child_resolve(children, pid)))
goto exit;
if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
@@ -121,6 +121,7 @@ afp_child_t *server_child_add(server_child_t *children, pid_t pid, int ipc_fd)
child->afpch_pid = pid;
child->afpch_ipc_fd = ipc_fd;
+ child->afpch_logintime = time(NULL);
hash_child(children->servch_table, child);
children->servch_count++;
@@ -136,9 +137,11 @@ int server_child_remove(server_child_t *children, pid_t pid)
int fd;
afp_child_t *child;
- if (!(child = resolve_child(children->servch_table, pid)))
+ if (!(child = server_child_resolve(children, pid)))
return -1;
+ pthread_mutex_lock(&children->servch_lock);
+
unhash_child(child);
if (child->afpch_clientid) {
free(child->afpch_clientid);
@@ -153,8 +156,7 @@ int server_child_remove(server_child_t *children, pid_t pid)
free(child);
children->servch_count--;
- if (children->servch_cleanup)
- children->servch_cleanup(pid);
+ pthread_mutex_unlock(&children->servch_lock);
return fd;
}
@@ -173,6 +175,8 @@ void server_child_free(server_child_t *children)
close(child->afpch_ipc_fd);
if (child->afpch_clientid)
free(child->afpch_clientid);
+ if (child->afpch_volumes)
+ free(child->afpch_volumes);
free(child);
child = tmp;
}
@@ -225,7 +229,7 @@ int server_child_transfer_session(server_child_t *children,
EC_INIT;
afp_child_t *child;
- if ((child = resolve_child(children->servch_table, pid)) == NULL) {
+ if ((child = server_child_resolve(children, pid)) == NULL) {
LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
if (kill(pid, 0) == 0) {
LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
@@ -274,13 +278,15 @@ void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
afp_child_t *child, *tmp;
int i;
+ pthread_mutex_lock(&children->servch_lock);
+
for (i = 0; i < CHILD_HASHSIZE; i++) {
child = children->servch_table[i];
while (child) {
tmp = child->afpch_next;
if (child->afpch_pid != pid) {
if (child->afpch_idlen == idlen && memcmp(child->afpch_clientid, id, idlen) == 0) {
- if ( child->afpch_time != boottime ) {
+ if ( child->afpch_boottime != boottime ) {
/* Client rebooted */
if (uid == child->afpch_uid) {
kill_child(child);
@@ -299,7 +305,7 @@ void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
}
} else {
/* update childs own slot */
- child->afpch_time = boottime;
+ child->afpch_boottime = boottime;
if (child->afpch_clientid)
free(child->afpch_clientid);
LOG(log_debug, logtype_default, "Setting client ID for %u", child->afpch_pid);
@@ -311,6 +317,8 @@ void server_child_kill_one_by_id(server_child_t *children, pid_t pid,
child = tmp;
}
}
+
+ pthread_mutex_unlock(&children->servch_lock);
}
/* ---------------------------
diff --git a/libatalk/util/server_ipc.c b/libatalk/util/server_ipc.c
index 838cece7..fa1778e9 100644
--- a/libatalk/util/server_ipc.c
+++ b/libatalk/util/server_ipc.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
@@ -104,6 +105,45 @@ static int ipc_get_session(struct ipc_header *ipc, server_child_t *children)
return 0;
}
+static int ipc_set_state(struct ipc_header *ipc, server_child_t *children)
+{
+ EC_INIT;
+ afp_child_t *child;
+
+ pthread_mutex_lock(&children->servch_lock);
+
+ if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
+ EC_FAIL;
+
+ memcpy(&child->afpch_state, ipc->msg, sizeof(uint16_t));
+
+EC_CLEANUP:
+ pthread_mutex_unlock(&children->servch_lock);
+ EC_EXIT;
+}
+
+static int ipc_set_volumes(struct ipc_header *ipc, server_child_t *children)
+{
+ EC_INIT;
+ afp_child_t *child;
+
+ pthread_mutex_lock(&children->servch_lock);
+
+ if ((child = server_child_resolve(children, ipc->child_pid)) == NULL)
+ EC_FAIL;
+
+ if (child->afpch_volumes) {
+ free(child->afpch_volumes);
+ child->afpch_volumes = NULL;
+ }
+ if (ipc->len)
+ child->afpch_volumes = strdup(ipc->msg);
+
+EC_CLEANUP:
+ pthread_mutex_unlock(&children->servch_lock);
+ EC_EXIT;
+}
+
/***********************************************************************************
* Public functions
***********************************************************************************/
@@ -201,6 +241,16 @@ int ipc_server_read(server_child_t *children, int fd)
return -1;
break;
+ case IPC_STATE:
+ if (ipc_set_state(&ipc, children) != 0)
+ return -1;
+ break;
+
+ case IPC_VOLUMES:
+ if (ipc_set_volumes(&ipc, children) != 0)
+ return -1;
+ break;
+
default:
LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
return -1;
@@ -252,3 +302,8 @@ int ipc_child_write(int fd, uint16_t command, int len, void *msg)
return 0;
}
+
+int ipc_child_state(AFPObj *obj, uint16_t state)
+{
+ return ipc_child_write(obj->ipc_fd, IPC_STATE, sizeof(uint16_t), &state);
+}
diff --git a/macros/netatalk.m4 b/macros/netatalk.m4
index 72cbaebd..b0f3d8e2 100644
--- a/macros/netatalk.m4
+++ b/macros/netatalk.m4
@@ -1,5 +1,29 @@
dnl Kitchen sink for configuration macros
+dnl Check for dbus-glib, for AFP stats
+AC_DEFUN([AC_NETATALK_DBUS_GLIB], [
+ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
+ PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0 >= 2.6, have_dbus_glib=yes, have_dbus_glib=no)
+ AC_SUBST(DBUS_CFLAGS)
+ AC_SUBST(DBUS_LIBS)
+ AC_SUBST(DBUS_GLIB_CFLAGS)
+ AC_SUBST(DBUS_GLIB_LIBS)
+ AM_CONDITIONAL(HAVE_DBUS_GLIB, test x$have_dbus_glib = xyes -a x$have_dbus = xyes)
+
+ AC_ARG_WITH(
+ dbus-sysconf-dir,
+ [AS_HELP_STRING([--with-dbus-sysconf-dir],[Path to dbus system bus security configuration directory (default: ${sysconfdir}/dbus-1/system.d/)])],
+ ac_cv_dbus_sysdir=$withval,
+ ac_cv_dbus_sysdir='${sysconfdir}/dbus-1/system.d'
+ )
+
+ if test x$have_dbus_glib = xyes -a x$have_dbus = xyes ; then
+ AC_DEFINE(HAVE_DBUS_GLIB, 1, [Define if support for dbus-glib was found])
+ DBUS_SYS_DIR="$ac_cv_dbus_sysdir"
+ AC_SUBST(DBUS_SYS_DIR)
+ fi
+])
+
dnl Whether to enable developer build
AC_DEFUN([AC_DEVELOPER], [
AC_MSG_CHECKING([whether to enable developer build])
diff --git a/macros/pam-check.m4 b/macros/pam-check.m4
index 17a92898..26d14b85 100644
--- a/macros/pam-check.m4
+++ b/macros/pam-check.m4
@@ -138,6 +138,13 @@ AC_DEFUN([AC_NETATALK_PATH_PAM], [
AC_DEFINE(USE_PAM, 1, [Define to enable PAM support])
fi
+ AC_ARG_WITH(
+ pam-confdir,
+ [AS_HELP_STRING([--with-pam-confdir=PATH],[Path to PAM config dir (default: $sysconfdir/pam.d)])],
+ PAMDIR=$withval,
+ PAMDIR='${sysconfdir}/pam.d'
+ )
+
LIB_REMOVE_USR_LIB(PAM_LIBS)
CFLAGS_REMOVE_USR_INCLUDE(PAM_CFLAGS)
AC_SUBST(PAMDIR)
diff --git a/macros/summary.m4 b/macros/summary.m4
index f4d99863..460efc66 100644
--- a/macros/summary.m4
+++ b/macros/summary.m4
@@ -55,6 +55,8 @@ dnl AC_MSG_RESULT([ Samba sharemode interop: $neta_cv_have_smbshmd])
AC_MSG_RESULT([ ACL support: $with_acl_support])
AC_MSG_RESULT([ Kerberos support: $with_kerberos])
AC_MSG_RESULT([ LDAP support: $netatalk_cv_ldap])
+ AC_MSG_RESULT([ dbus support: $have_dbus_glib])
+ AC_MSG_RESULT([ dbus system directory: $ac_cv_dbus_sysdir])
if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
AC_MSG_RESULT([])
AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])