#ifdef HAVE_AVAHI
#include <unistd.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+
#include "afp_avahi.h"
+#include "afp_config.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
static void publish_reply(AvahiEntryGroup *g,
AvahiEntryGroupState state,
* This function tries to register the AFP DNS
* SRV service type.
*/
-static void register_stuff(struct context *ctx) {
- char r[128];
- int ret;
-
+static void register_stuff(void) {
+ const AFPConfig *config;
+ DSI *dsi;
assert(ctx->client);
if (!ctx->group) {
- if (!(ctx->group = avahi_entry_group_new(ctx->client,
- publish_reply,
- ctx))) {
+ if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
avahi_strerror(avahi_client_errno(ctx->client)));
goto fail;
}
-
}
- LOG(log_info, logtype_afpd, "Adding service '%s'", ctx->name);
-
if (avahi_entry_group_is_empty(ctx->group)) {
/* Register our service */
- if (avahi_entry_group_add_service(ctx->group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- ctx->name,
- AFP_DNS_SERVICE_TYPE,
- NULL,
- NULL,
- ctx->port,
- NULL) < 0) {
- LOG(log_error, logtype_afpd, "Failed to add service: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
+ /* AFP server */
+ for (config = ctx->configs; config; config = config->next) {
+ dsi = (DSI *)config->obj.handle;
+ if (avahi_entry_group_add_service(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ config->obj.options.server ?
+ config->obj.options.server
+ : config->obj.options.hostname,
+ AFP_DNS_SERVICE_TYPE,
+ NULL,
+ NULL,
+ getip_port((struct sockaddr *)&dsi->server),
+ NULL) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ } /* for config*/
+
+ /* AFP volumes */
+
+ if (avahi_entry_group_commit(ctx->group) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
- if (avahi_entry_group_commit(ctx->group) < 0) {
- LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
- avahi_strerror(avahi_client_errno(ctx->client)));
- goto fail;
- }
- }
+ } /* if avahi_entry_group_is_empty*/
return;
AvahiEntryGroupState state,
AVAHI_GCC_UNUSED void *userdata)
{
- struct context *ctx = userdata;
- char *n;
-
- assert(g == ctx->group);
+ assert(ctx->group == NULL || g == ctx->group);
switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED :
/* The entry group has been established successfully */
+ LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
break;
case AVAHI_ENTRY_GROUP_COLLISION:
- /* Pick a new name for our service */
- n = avahi_alternative_service_name(ctx->name);
- assert(n);
- avahi_free(ctx->name);
- ctx->name = n;
- register_stuff(ctx);
+ /* With multiple names there's no way to know which one collided */
+ LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ avahi_client_free(avahi_entry_group_get_client(g));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
break;
case AVAHI_ENTRY_GROUP_FAILURE:
AvahiClientState state,
void *userdata)
{
- struct context *ctx = userdata;
-
ctx->client = client;
switch (state) {
/* The server has startup successfully and registered its host
* name on the network, so it's time to create our services */
if (!ctx->group)
- register_stuff(ctx);
+ register_stuff();
break;
case AVAHI_CLIENT_S_COLLISION:
ctx,
&error))) {
- LOG(log_error, logtype_afpd, "Failed to contact server: %s\n",
+ LOG(log_error, logtype_afpd, "Failed to contact server: %s",
avahi_strerror(error));
avahi_client_free (ctx->client);
}
} else {
- LOG(log_error, logtype_afpd, "Client failure: %s\n",
+ LOG(log_error, logtype_afpd, "Client failure: %s",
avahi_strerror(avahi_client_errno(client)));
avahi_client_free (ctx->client);
avahi_threaded_poll_quit(ctx->threaded_poll);
}
}
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
/*
* Tries to setup the Zeroconf thread and any
* neccessary config setting.
*/
-void* av_zeroconf_setup(unsigned long port, const char *name) {
- struct context *ctx = NULL;
-
- /* default service name, if there's none in
- * the config file.
- */
- char service[256] = "AFP Server on ";
+void av_zeroconf_setup(const AFPConfig *configs) {
int error, ret;
- /* initialize the struct that holds our
- * config settings.
- */
- ctx = malloc(sizeof(struct context));
+ /* initialize the struct that holds our config settings. */
+ if (!ctx) {
+ ctx = calloc(1, sizeof(struct context));
+ ctx->configs = configs;
+ }
assert(ctx);
- ctx->client = NULL;
- ctx->group = NULL;
- ctx->threaded_poll = NULL;
- ctx->thread_running = 0;
-
- LOG(log_debug, logtype_afpd, "Setting port for Zeroconf service to: %i.", port);
- ctx->port = port;
-
- /* Prepare service name */
- if (!name) {
- LOG(log_debug, logtype_afpd, "Assigning default service name.");
- gethostname(service+14, sizeof(service)-15);
- service[sizeof(service)-1] = 0;
- ctx->name = strdup(service);
- } else {
- ctx->name = strdup(name);
- }
-
- assert(ctx->name);
/* first of all we need to initialize our threading env */
if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
AVAHI_CLIENT_NO_FAIL,
client_callback,
- ctx,
+ NULL,
&error))) {
LOG(log_error, logtype_afpd, "Failed to create client object: %s",
avahi_strerror(avahi_client_errno(ctx->client)));
goto fail;
}
- return ctx;
+ return;
fail:
if (ctx)
- av_zeroconf_unregister(ctx);
+ av_zeroconf_unregister();
- return NULL;
+ return;
}
/*
* This function finally runs the loop impl.
*/
-int av_zeroconf_run(void *u) {
- struct context *ctx = u;
+int av_zeroconf_run(void) {
int ret;
/* Finally, start the event loop thread */
if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
- LOG(log_error,
- logtype_afpd,
- "Failed to create thread: %s",
+ LOG(log_error, logtype_afpd, "Failed to create thread: %s",
avahi_strerror(avahi_client_errno(ctx->client)));
goto fail;
} else {
fail:
if (ctx)
- av_zeroconf_unregister(ctx);
+ av_zeroconf_unregister();
return -1;
}
* Tries to shutdown this loop impl.
* Call this function from outside this thread.
*/
-void av_zeroconf_shutdown(void *u) {
- struct context *ctx = u;
-
+void av_zeroconf_shutdown() {
/* Call this when the app shuts down */
avahi_threaded_poll_stop(ctx->threaded_poll);
- avahi_free(ctx->name);
avahi_client_free(ctx->client);
avahi_threaded_poll_free(ctx->threaded_poll);
}
* Tries to shutdown this loop impl.
* Call this function from inside this thread.
*/
-int av_zeroconf_unregister(void *u) {
- struct context *ctx = u;
-
+int av_zeroconf_unregister() {
if (ctx->thread_running) {
/* First, block the event loop */
avahi_threaded_poll_lock(ctx->threaded_poll);
ctx->thread_running = 0;
}
- avahi_free(ctx->name);
-
if (ctx->client)
avahi_client_free(ctx->client);
#include "afp_config.h"
#include "uam_auth.h"
#include "status.h"
+#include "volume.h"
+#include "afp_zeroconf.h"
#define LINESIZE 1024
return buf;
}
#endif /* USE_SRVLOC */
-#ifdef USE_ZEROCONF
-#include "afp_zeroconf.h"
-#endif /* USE_ZEROCONF */
static void dsi_cleanup(const AFPConfig *config)
{
srvloc_dereg_err:
dsi->srvloc_url[0] = '\0';
SLPClose(hslp);
-#elif defined (USE_ZEROCONF)
- DSI *dsi = (DSI *)config->obj.handle;
-
- /* Do nothing if we didn't register. */
- if (!dsi || dsi->zeroconf_registered == 0)
- return;
-
- zeroconf_deregister();
#endif /* USE_SRVLOC */
}
}
#endif /* USE_SRVLOC */
-#ifdef USE_ZEROCONF
- dsi->zeroconf_registered = 0; /* Mark that we haven't registered. */
- if (!(options->flags & OPTION_NOZEROCONF)) {
- zeroconf_register(getip_port((struct sockaddr *)&dsi->server),
- options->server ? options->server : options->hostname);
- dsi->zeroconf_registered = 1; /* Mark that we have registered. */
- config->server_cleanup = dsi_cleanup;
- }
-#endif /* USE_ZEROCONF */
-
config->fd = dsi->serversock;
config->obj.handle = dsi;
config->obj.config = config;
/* allocate server configurations. this should really store the last
* entry in config->last or something like that. that would make
* supporting multiple dsi transports easier. */
-static AFPConfig *AFPConfigInit(const struct afp_options *options,
+static AFPConfig *AFPConfigInit(struct afp_options *options,
const struct afp_options *defoptions)
{
AFPConfig *config = NULL, *next = NULL;
}
#endif /* HAVE_ACLS */
- /* this should really get a head and a tail to simplify things. */
+ /* AFPConfigInit can return two linked configs due to DSI and ASP */
if (!first) {
if ((first = AFPConfigInit(&options, cmdline)))
config = first->next ? first->next : first;
if (!have_option)
first = AFPConfigInit(cmdline, cmdline);
+ /* Now register with zeroconf, we also need the volumes for that */
+ readvolfile(&first->obj, &first->obj.options.systemvol, NULL, 0, NULL);
+
+ zeroconf_register(first);
+
return first;
}
}
} else if (is_var(p, "$d")) {
q = path;
- } else if (is_var(p, "$f")) {
+ } else if (pwd && is_var(p, "$f")) {
if ((r = strchr(pwd->pw_gecos, ',')))
*r = '\0';
q = pwd->pw_gecos;
- } else if (is_var(p, "$g")) {
+ } else if (pwd && is_var(p, "$g")) {
struct group *grp = getgrgid(pwd->pw_gid);
if (grp)
q = grp->gr_name;
q = obj->options.server;
} else
q = obj->options.hostname;
- } else if (is_var(p, "$u")) {
+ } else if (obj->username && is_var(p, "$u")) {
char* sep = NULL;
if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
q = sep+1;
/* ----------------------
* Read a volume configuration file and add the volumes contained within to
- * the global volume list. If p2 is non-NULL, the file that is opened is
+ * the global volume list. This gets called from the forked afpd childs.
+ * The master now reads this too for Zeroconf announcements.
+ *
+ * If p2 is non-NULL, the file that is opened is
* p1/p2
*
* Lines that begin with # and blank lines are ignored.
* <unix path> [<volume name>] [allow:<user>,<@group>,...] \
* [codepage:<file>] [casefold:<num>]
* <extension> TYPE [CREATOR]
+ *
*/
-static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
+int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
{
FILE *fp;
char path[MAXPATHLEN + 1];
/* send path through variable substitution */
if (*path != '~') /* need to copy path to tmp */
strcpy(tmp, path);
- if (!pwent)
+ if (!pwent && obj->username)
pwent = getpwnam(obj->username);
volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL);