From 22ad101eb72bf10fe5144526209798c677b2d1d8 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 5 Feb 2013 17:25:27 +0100 Subject: [PATCH] AFP statistics via dbus IPC Fix PAM config installation. make distcheck was failing on Solaris in uninstall, because the uninstall target ran some `rm -f PAMFILE` where PAMFILE is hardcoded to /etc/pam.d. The resulting EPERM causes the make target to fail. --- NEWS | 7 + config/Makefile.am | 8 +- config/netatalk-dbus.conf | 17 +++ config/pam/.gitignore | 2 +- config/pam/Makefile.am | 28 +--- .../pam/{netatalk.pam.tmpl => netatalk.tmpl} | 0 configure.ac | 3 + contrib/shell_utils/Makefile.am | 4 +- contrib/shell_utils/afpstats | 29 +++++ etc/afpd/Makefile.am | 23 +++- etc/afpd/afp_dsi.c | 15 +-- etc/afpd/afpstats-service.xml | 8 ++ etc/afpd/afpstats.c | 118 +++++++++++++++++ etc/afpd/afpstats.h | 23 ++++ etc/afpd/afpstats_obj.c | 110 ++++++++++++++++ etc/afpd/afpstats_obj.h | 19 +++ etc/afpd/afpstats_service_glue.h | 121 ++++++++++++++++++ etc/afpd/auth.c | 3 + etc/afpd/main.c | 6 + etc/afpd/volume.c | 28 ++++ include/atalk/dsi.h | 3 - include/atalk/server_child.h | 7 +- include/atalk/server_ipc.h | 3 + libatalk/dsi/dsi_stream.c | 4 - libatalk/util/server_child.c | 26 ++-- libatalk/util/server_ipc.c | 55 ++++++++ macros/netatalk.m4 | 24 ++++ macros/pam-check.m4 | 7 + macros/summary.m4 | 2 + 29 files changed, 648 insertions(+), 55 deletions(-) create mode 100644 config/netatalk-dbus.conf rename config/pam/{netatalk.pam.tmpl => netatalk.tmpl} (100%) create mode 100755 contrib/shell_utils/afpstats create mode 100644 etc/afpd/afpstats-service.xml create mode 100644 etc/afpd/afpstats.c create mode 100644 etc/afpd/afpstats.h create mode 100644 etc/afpd/afpstats_obj.c create mode 100644 etc/afpd/afpstats_obj.h create mode 100644 etc/afpd/afpstats_service_glue.h 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]) -- 2.39.2