Changes in 2.2
==============
+* NEW: afpd: new volume option "nonetids"
+* NEW: afpd: ACL access check caching
+* NEW: afpd: FCE event notifications
+* NEW: afpd: new option "-mimicmodel" for specifying Bonjour model registration
* UPD: Support for Berkeley DB 5.1
* UPD: case-conversion is based on Unicode 6.0.0
* UPD: cnid_metad: allow up to 4096 volumes
* UPD: afpd: only forward SIGTERM and SIGUSR1 from parent to childs
+* UPD: afpd: use internal function instead of popening du -sh in order to
+ calculate the used size of a volume for option "volsizelimit"
+* UPD: afpd: Add negative UUID caching, enhance local UUID handling
* FIX: afpd: configuration reload with SIGHUP
* FIX: afpd: crashes in the dircache
* FIX: afpd: Correct afp logout vs dsi eof behaviour
#ifndef AD_H
#define AD_H
-#define _XOPEN_SOURCE 600
+#if !defined(__FreeBSD__)
+# define _XOPEN_SOURCE 600
+#endif
#include <sys/types.h>
#include <sys/stat.h>
islink = !fdval && S_ISLNK(fs->st_mode);
mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
+#if defined(__FreeBSD__)
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+#else
TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
+#endif
+
if (utimes(to.p_path, tv)) {
SLOG("utimes: %s", to.p_path);
rval = 1;
Makefile.in
netacnv
afpldaptest
+fce
logger_test
.deps
.libs
pkgconfdir = @PKGCONFDIR@
bin_PROGRAMS =
-noinst_PROGRAMS = netacnv logger_test
+noinst_PROGRAMS = netacnv logger_test fce
netacnv_SOURCES = netacnv.c
netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
logger_test_SOURCES = logger_test.c
logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la
+fce_SOOURCE = fce.c
+fce_LDADD = $(top_builddir)/libatalk/libatalk.la
+fce_CFLAGS = -I$(top_srcdir)/include
+
bin_PROGRAMS += afpldaptest
afpldaptest_SOURCES = uuidtest.c
afpldaptest_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include <atalk/fce_api.h>
+#include <atalk/util.h>
+
+#define MAXBUFLEN 1024
+
+static char *fce_ev_names[] = {
+ "",
+ "FCE_FILE_MODIFY",
+ "FCE_FILE_DELETE",
+ "FCE_DIR_DELETE",
+ "FCE_FILE_CREATE",
+ "FCE_DIR_CREATE",
+ "FCE_TM_SIZE"
+};
+
+// get sockaddr, IPv4 or IPv6:
+static void *get_in_addr(struct sockaddr *sa)
+{
+ if (sa->sa_family == AF_INET) {
+ return &(((struct sockaddr_in*)sa)->sin_addr);
+ }
+
+ return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
+
+static int unpack_fce_packet(unsigned char *buf, struct fce_packet *packet)
+{
+ unsigned char *p = buf;
+
+ memcpy(&packet->magic[0], p, sizeof(packet->magic));
+ p += sizeof(packet->magic);
+
+ packet->version = *p;
+ p++;
+
+ packet->mode = *p;
+ p++;
+
+ memcpy(&packet->event_id, p, sizeof(packet->event_id));
+ p += sizeof(packet->event_id);
+ packet->event_id = ntohl(packet->event_id);
+
+ memcpy(&packet->datalen, p, sizeof(packet->datalen));
+ p += sizeof(packet->datalen);
+ packet->datalen = ntohs(packet->datalen);
+
+ memcpy(&packet->data[0], p, packet->datalen);
+ p += packet->datalen;
+
+ return 0;
+}
+
+int main(void)
+{
+ int sockfd;
+ struct addrinfo hints, *servinfo, *p;
+ int rv;
+ int numbytes;
+ struct sockaddr_storage their_addr;
+ char buf[MAXBUFLEN];
+ socklen_t addr_len;
+ char s[INET6_ADDRSTRLEN];
+ uint64_t tmsize;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if ((rv = getaddrinfo(NULL, FCE_DEFAULT_PORT_STRING, &hints, &servinfo)) != 0) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+ return 1;
+ }
+
+ // loop through all the results and bind to the first we can
+ for(p = servinfo; p != NULL; p = p->ai_next) {
+ if ((sockfd = socket(p->ai_family, p->ai_socktype,
+ p->ai_protocol)) == -1) {
+ perror("listener: socket");
+ continue;
+ }
+
+ if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ close(sockfd);
+ perror("listener: bind");
+ continue;
+ }
+
+ break;
+ }
+
+ if (p == NULL) {
+ fprintf(stderr, "listener: failed to bind socket\n");
+ return 2;
+ }
+
+ freeaddrinfo(servinfo);
+
+ printf("listener: waiting to recvfrom...\n");
+
+ addr_len = sizeof their_addr;
+
+ struct fce_packet packet;
+ while (1) {
+ if ((numbytes = recvfrom(sockfd,
+ buf,
+ MAXBUFLEN - 1,
+ 0,
+ (struct sockaddr *)&their_addr,
+ &addr_len)) == -1) {
+ perror("recvfrom");
+ exit(1);
+ }
+
+ unpack_fce_packet(buf, &packet);
+
+ if (memcmp(packet.magic, FCE_PACKET_MAGIC, sizeof(packet.magic)) == 0) {
+
+ switch (packet.mode) {
+ case FCE_TM_SIZE:
+ memcpy(&tmsize, packet.data, sizeof(uint64_t));
+ tmsize = ntoh64(tmsize);
+ printf("ID: %" PRIu32 ", Event: %s, Volume: %s, TM used size: %" PRIu64 " \n",
+ packet.event_id, fce_ev_names[packet.mode], packet.data + sizeof(uint64_t), tmsize);
+ break;
+
+ case FCE_CONN_START:
+ printf("FCE Start\n");
+ break;
+
+ case FCE_CONN_BROKEN:
+ printf("Broken FCE connection\n");
+ break;
+
+ default:
+ printf("ID: %" PRIu32 ", Event: %s, Path: %s\n",
+ packet.event_id, fce_ev_names[packet.mode], packet.data);
+ break;
+ }
+ }
+ }
+
+ close(sockfd);
+ return 0;
+}
ret = getnamefromuuid( uuid, &name, &type);
if (ret == 0) {
switch (type) {
- case UUID_LOCAL:
- printf("local UUID: %s\n", optarg);
- break;
case UUID_USER:
printf("UUID: %s ==> User: %s\n", optarg, name);
break;
case UUID_GROUP:
printf("UUID: %s ==> Group: %s\n", optarg, name);
break;
+ default:
+ printf("???: %s\n", optarg);
+ break;
}
free(name);
} else {
# in dotted-decimal format for IPv4 or in
# hexadecimal format for IPv6.
#
+# Avahi (Bonjour) related options:
+# -mimicmodel <model>
+# Specifies the icon model that appears on
+# clients. Defaults to off. Examples: RackMac
+# (same as Xserve), PowerBook, PowerMac, Macmini,
+# iMac, MacBook, MacBookPro, MacBookAir, MacPro,
+# AppleTV1,1, AirPort
+#
#
Makefile
Makefile.in
afpd
+fce
hash
test_parse_mtab
.deps
pkgconfdir = @PKGCONFDIR@
sbin_PROGRAMS = afpd
-noinst_PROGRAMS = hash
+noinst_PROGRAMS = hash fce
afpd_SOURCES = \
afp_avahi.c \
directory.c \
enumerate.c \
extattrs.c \
+ fce_api.c \
+ fce_util.c \
file.c \
filedir.c \
fork.c \
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
- filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.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
hash_SOURCES = hash.c
hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
+
+fce_SOURCES = fce_api.c fce_util.c
+fce_CFLAGS = -DFCE_TEST_MAIN -I$(top_srcdir)/include
+fce_LDADD = $(top_builddir)/libatalk/libatalk.la
#endif
#ifdef HAVE_POSIX_ACLS
#include <sys/acl.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
/* uid/gid first */
EC_ZERO(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
switch (uuidtype) {
- case UUID_LOCAL:
- free(name);
- name = NULL;
- darwin_aces++;
- continue;
case UUID_USER:
EC_NULL_LOG(pwd = getpwnam(name));
nfsv4_aces->a_who = pwd->pw_uid;
nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
break;
+ default:
+ LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: unkown uuidtype");
+ EC_FAIL;
}
free(name);
name = NULL;
nfsv4_ace_rights |= darwin_to_nfsv4_rights[i].to;
}
- LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x", darwin_ace_flags, nfsv4_ace_flags);
- LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x", darwin_ace_rights, nfsv4_ace_rights);
+ LOG(log_debug9, logtype_afpd,
+ "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x",
+ darwin_ace_flags, nfsv4_ace_flags);
+ LOG(log_debug9, logtype_afpd,
+ "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x",
+ darwin_ace_rights, nfsv4_ace_rights);
nfsv4_aces->a_flags = nfsv4_ace_flags;
nfsv4_aces->a_access_mask = nfsv4_ace_rights;
EC_ZERO_LOG(acl_get_permset(e, &permset));
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_READ))
+#else
if (acl_get_perm(permset, ACL_READ))
+#endif
rights = DARWIN_ACE_READ_DATA
| DARWIN_ACE_READ_EXTATTRIBUTES
| DARWIN_ACE_READ_ATTRIBUTES
| DARWIN_ACE_READ_SECURITY;
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_WRITE)) {
+#else
if (acl_get_perm(permset, ACL_WRITE)) {
+#endif
rights |= DARWIN_ACE_WRITE_DATA
| DARWIN_ACE_APPEND_DATA
| DARWIN_ACE_WRITE_EXTATTRIBUTES
if (is_dir)
rights |= DARWIN_ACE_DELETE_CHILD;
}
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_EXECUTE))
+#else
if (acl_get_perm(permset, ACL_EXECUTE))
+#endif
rights |= DARWIN_ACE_EXECUTE;
EC_CLEANUP:
/* uid/gid */
EC_ZERO_LOG(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
switch (uuidtype) {
- case UUID_LOCAL:
- free(name);
- name = NULL;
- continue;
case UUID_USER:
EC_NULL_LOG(pwd = getpwnam(name));
tag = ACL_USER;
id = (uid_t)(grp->gr_gid);
LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
break;
+ default:
+ continue;
}
free(name);
name = NULL;
/* for files def_acl will be NULL */
/* create access acl from mode */
+#ifdef HAVE_ACL_FROM_MODE
EC_NULL_LOG_ERR(acc_acl = acl_from_mode(st.st_mode), AFPERR_MISC);
-
+#else
+#error "Missing acl_from_mode() replacement"
+#endif
/* adds the clients aces */
EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &def_acl, &acc_acl, ace_count), AFPERR_MISC);
* Note: this gets called frequently and is a good place for optimizations !
*
* @param vol (r) volume
- * @param dir (r) directory
+ * @param dir (rw) directory
* @param path (r) path to filesystem object
* @param uuid (r) UUID of user
* @param requested_rights (r) requested Darwin ACE
* @returns AFP result code
*/
static int check_acl_access(const struct vol *vol,
- const struct dir *dir,
+ struct dir *dir,
const char *path,
const uuidp_t uuid,
uint32_t requested_rights)
LOG(log_warning, logtype_afpd, "check_access: afp_access not supported for groups");
EC_STATUS(AFPERR_MISC);
goto EC_CLEANUP;
-
- case UUID_LOCAL:
- LOG(log_warning, logtype_afpd, "check_access: local UUID");
+ default:
EC_STATUS(AFPERR_MISC);
goto EC_CLEANUP;
}
+ if ((strcmp(path, ".") == 0) && (dir->d_rights_cache != 0xffffffff)) {
+ /* its a dir and the cache value is valid */
+ allowed_rights = dir->d_rights_cache;
+ LOG(log_debug, logtype_afpd, "allowed rights from dircache: 0x%08x", allowed_rights);
+ } else {
#ifdef HAVE_SOLARIS_ACLS
- EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
#endif
#ifdef HAVE_POSIX_ACLS
- EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
#endif
+ /*
+ * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
+ * directory. As it seems the 10.6 AFP client is puzzled when this right is not
+ * allowed where a delete would succeed because the parent dir gives write perms.
+ * So we check the parent dir for write access and set the right accordingly.
+ * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
+ * there to do this extra check -- afaict.
+ */
+ if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
+ int i;
+ uint32_t parent_rights = 0;
+
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /* use volume path */
+ EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
+ } else {
+ /* build path for parent */
+ EC_NULL_LOG_ERR(parent = bstrcpy(dir->d_fullpath), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
+ EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
+ }
- LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
-
- /*
- * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
- * directory. As it seems the 10.6 AFP client is puzzled when this right is not
- * allowed where a delete would succeed because the parent dir gives write perms.
- * So we check the parent dir for write access and set the right accordingly.
- * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
- * there to do this extra check -- afaict.
- */
- if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
- int i;
- uint32_t parent_rights = 0;
-
- if (dir->d_did == DIRDID_ROOT_PARENT) {
- /* use volume path */
- EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
- } else {
- /* build path for parent */
- EC_NULL_LOG_ERR(parent = bstrcpy(dir->d_fullpath), AFPERR_MISC);
- EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
- EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
- EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
- EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
- }
-
- LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
- EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
+ LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
+ EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
#ifdef HAVE_SOLARIS_ACLS
- EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
+ EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
#endif
#ifdef HAVE_POSIX_ACLS
- EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
#endif
- if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
- allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
+ if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
+ allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
+ }
+ LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
+ dir->d_rights_cache = allowed_rights;
}
if ((requested_rights & allowed_rights) != requested_rights) {
DSI *dsi;
char name[MAXINSTANCENAMELEN+1];
AvahiStringList *strlist = NULL;
+ AvahiStringList *strlist2 = NULL;
char tmpname[256];
assert(ctx->client);
avahi_strerror(avahi_client_errno(ctx->client)));
goto fail;
} /* if */
+
+ if (config->obj.options.mimicmodel) {
+ strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", config->obj.options.mimicmodel);
+ if (avahi_entry_group_add_service_strlst(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ dsi->bonjourname,
+ DEV_INFO_SERVICE_TYPE,
+ NULL,
+ NULL,
+ 0,
+ strlist2) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+ } /* if (config->obj.options.mimicmodel) */
+
} /* for config*/
if (avahi_entry_group_commit(ctx->group) < 0) {
#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
#define ADISK_SERVICE_TYPE "_adisk._tcp"
+#define DEV_INFO_SERVICE_TYPE "_device-info._tcp"
#define MAXINSTANCENAMELEN 63
#include <atalk/ldapconfig.h>
#endif
-#include "globals.h"
+#include <atalk/globals.h>
#include "afp_config.h"
#include "uam_auth.h"
#include "status.h"
#define AFPD_CONFIG_H 1
#include <atalk/server_child.h>
-
-#include "globals.h"
+#include <atalk/globals.h>
typedef struct AFPConfig {
AFPObj obj;
#endif /* HAVE_SYS_STAT_H */
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <atalk/logger.h>
#include <setjmp.h>
+#include <time.h>
+#include <atalk/logger.h>
#include <atalk/dsi.h>
#include <atalk/compat.h>
#include <atalk/util.h>
#include <atalk/locking.h>
+#include <atalk/uuid.h>
+#include <atalk/paths.h>
+#include <atalk/server_ipc.h>
+#include <atalk/fce_api.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "switch.h"
#include "auth.h"
#include "fork.h"
}
}
+/* SIGQUIT handler */
+static void ipc_reconnect_handler(int sig _U_)
+{
+ DSI *dsi = (DSI *)AFPobj->handle;
+
+ if (reconnect_ipc(AFPobj) != 0) {
+ LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC reconnect");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ if (ipc_child_write(AFPobj->ipc_fd, IPC_GETSESSION, AFPobj->sinfo.clientid_len, AFPobj->sinfo.clientid) != 0) {
+ LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC ID resend");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+ LOG(log_note, logtype_afpd, "ipc_reconnect_handler: IPC reconnect done");
+}
+
/* SIGURG handler (primary reconnect) */
static void afp_dsi_transfer_session(int sig _U_)
{
}
if (dsi->flags & DSI_DISCONNECTED) {
+ if (geteuid() == 0) {
+ LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
+ afp_dsi_die(EXITERR_CLNT);
+ }
if (dsi->tickle > AFPobj->options.disconnected) {
LOG(log_error, logtype_afpd, "afp_alarm: reconnect timer expired, goodbye");
afp_dsi_die(EXITERR_CLNT);
/* if we're in the midst of processing something, don't die. */
if ( !(dsi->flags & DSI_RUNNING) && (dsi->tickle >= AFPobj->options.timeout)) {
LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
- dsi->proto_close(dsi);
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
return;
}
LOG(log_debug, logtype_afpd, "afp_alarm: sending DSI tickle");
err = dsi_tickle(AFPobj->handle);
if (err <= 0) {
+ if (geteuid() == 0) {
+ LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
+ afp_dsi_die(EXITERR_CLNT);
+ }
LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
- dsi->proto_close(dsi);
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
}
struct sigaction action;
AFPobj = obj;
+ dsi->AFPobj = obj;
obj->exit = afp_dsi_die;
obj->reply = (int (*)()) dsi_cmdreply;
obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
dsi->tickle = 0;
memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_flags = SA_RESTART;
/* install SIGHUP */
action.sa_handler = afp_dsi_reload;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* install SIGURG */
action.sa_handler = afp_dsi_transfer_session;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGURG, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* install SIGTERM */
action.sa_handler = afp_dsi_die;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
- /* Added for server message support */
+ /* install SIGQUIT */
+ action.sa_handler = ipc_reconnect_handler;
+ if ( sigaction(SIGQUIT, &action, NULL ) < 0 ) {
+ LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
+ afp_dsi_die(EXITERR_SYS);
+ }
+
+ /* SIGUSR2 - server message support */
action.sa_handler = afp_dsi_getmesg;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGINT);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR2, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* SIGUSR1 - set down in 5 minutes */
action.sa_handler = afp_dsi_timedown;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
/* SIGINT - enable max_debug LOGging to /tmp/afpd.PID.XXXXXX */
action.sa_handler = afp_dsi_debug;
- sigfillset( &action.sa_mask );
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGINT, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
#ifndef DEBUGGING
- /* tickle handler */
+ /* SIGALRM - tickle handler */
action.sa_handler = alarm_handler;
- sigemptyset(&action.sa_mask);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ((sigaction(SIGALRM, &action, NULL) < 0) ||
(setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
afp_dsi_die(EXITERR_SYS);
dsi->flags &= ~DSI_RECONSOCKET;
continue;
}
- /* Some error on the client connection, enter disconnected state */
- dsi->flags |= DSI_DISCONNECTED;
/* the client sometimes logs out (afp_logout) but doesn't close the DSI session */
if (dsi->flags & DSI_AFP_LOGGED_OUT) {
+ LOG(log_note, logtype_afpd, "afp_over_dsi: client logged out, terminating DSI session");
afp_dsi_close(obj);
exit(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);
+ }
+
+ /* Some error on the client connection, enter disconnected state */
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
+
pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
continue; /* continue receiving until disconnect timer expires
* or a primary reconnect succeeds */
debug_request = 0;
dircache_dump();
+ uuidcache_dump();
if (debugging) {
if (obj->options.logconfig)
if (!dsi_cmdreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
break;
if (!dsi_wrtreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
break;
break;
}
pending_request(dsi);
+
+ vol_fce_tm_event();
}
/* error */
#include <atalk/paths.h>
#include <atalk/util.h>
#include <atalk/compat.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
-#include "globals.h"
#include "status.h"
#include "auth.h"
#include "dircache.h"
free(opt->ntseparator);
if (opt->logconfig && (opt->logconfig != save->logconfig))
free(opt->logconfig);
+ if (opt->mimicmodel && (opt->mimicmodel != save->mimicmodel))
+ free(opt->mimicmodel);
}
/* initialize options */
options->tcp_sndbuf = 0; /* 0 means don't change OS default */
options->tcp_rcvbuf = 0; /* 0 means don't change OS default */
options->dsireadbuf = 12;
+ options->mimicmodel = NULL;
}
/* parse an afpd.conf line. i'm doing it this way because it's
if ((c = getoption(buf, "-tcprcvbuf")))
options->tcp_rcvbuf = atoi(c);
+ if ((c = getoption(buf, "-fcelistener"))) {
+ LOG(log_note, logtype_afpd, "Adding fce listener \"%s\"", c);
+ fce_add_udp_socket(c);
+ }
+ if ((c = getoption(buf, "-fcecoalesce"))) {
+ LOG(log_note, logtype_afpd, "Fce coalesce: %s", c);
+ fce_set_coalesce(c);
+ }
+ if ((c = getoption(buf, "-fceevents"))) {
+ LOG(log_note, logtype_afpd, "Fce events: %s", c);
+ fce_set_events(c);
+ }
+
+ if ((c = getoption(buf, "-mimicmodel")) && (opt = strdup(c)))
+ options->mimicmodel = opt;
+
return 1;
}
#endif /* HAVE_CONFIG_H */
#include <atalk/afp.h>
-#include "globals.h"
+#include <atalk/globals.h>
const char *AfpNum2name(int num)
{
#include <atalk/afp.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "volume.h"
-#include "globals.h"
#include "directory.h"
#include "file.h"
#include "desktop.h"
#include <atalk/logger.h>
#include <atalk/server_ipc.h>
#include <atalk/uuid.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "auth.h"
#include "uam_auth.h"
#include "switch.h"
if (ibuflen < idlen || idlen > (90-10)) {
return AFPERR_PARAM;
}
- ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
+ if (!obj->sinfo.clientid) {
+ obj->sinfo.clientid = malloc(idlen + 8);
+ memcpy(obj->sinfo.clientid, p, idlen + 8);
+ obj->sinfo.clientid_len = idlen + 8;
+ }
+ if (ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p) != 0)
+ return AFPERR_MISC;
tklen = obj->sinfo.sessiontoken_len;
token = obj->sinfo.sessiontoken;
}
setitimer(ITIMER_REAL, &none, NULL);
/* check for old session, possibly transfering session from here to there */
- if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) == -1)
+ if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) != 0)
goto exit;
/* write uint16_t DSI request ID */
if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
#include <limits.h>
-#include "globals.h"
+#include <atalk/globals.h>
struct afp_versions {
char *av_name;
#include <atalk/util.h>
#include <atalk/bstradd.h>
#include <atalk/unicode.h>
+#include <atalk/globals.h>
#include "desktop.h"
#include "directory.h"
#include "dircache.h"
#include "file.h"
#include "volume.h"
-#include "globals.h"
#include "filedir.h"
#include "fork.h"
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/logger.h>
+#include <atalk/globals.h>
#include "volume.h"
#include "directory.h"
#include "fork.h"
-#include "globals.h"
#include "desktop.h"
#include "mangle.h"
#ifndef AFPD_DESKTOP_H
#define AFPD_DESKTOP_H 1
-#include "globals.h"
+#include <atalk/globals.h>
+
#include "volume.h"
struct savedt {
#include <atalk/queue.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "dircache.h"
#include "directory.h"
#include "hash.h"
-#include "globals.h"
+
/*
* Directory Cache
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
#include <atalk/errchk.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#include "directory.h"
#include "dircache.h"
#include "fork.h"
#include "file.h"
#include "filedir.h"
-#include "globals.h"
#include "unix.h"
#include "mangle.h"
#include "hash.h"
dir->dcache_ino = st->st_ino;
if (!S_ISDIR(st->st_mode))
dir->d_flags = DIRF_ISFILE;
+ dir->d_rights_cache = 0xffffffff;
return dir;
}
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) )
isad = 1;
+ if (ad.ad_md->adf_flags & O_CREAT) {
+ /* We just created it */
+ if (s_path->m_name == NULL) {
+ if ((s_path->m_name = utompath(vol,
+ upath,
+ dir->d_did,
+ utf8_encoding())) == NULL) {
+ LOG(log_error, logtype_afpd,
+ "getdirparams(\"%s\"): can't assign macname",
+ cfrombstr(dir->d_fullpath));
+ return AFPERR_MISC;
+ }
+ }
+ ad_setname(&ad, s_path->m_name);
+ ad_setid( &ad,
+ s_path->st.st_dev,
+ s_path->st.st_ino,
+ dir->d_did,
+ dir->d_pdid,
+ vol->v_stamp);
+ ad_flush( &ad);
+ }
+ }
}
pdid = dir->d_pdid;
ad_setname(&ad, s_path->m_name);
ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
+ fce_register_new_dir(s_path);
+
ad_flush( &ad);
ad_close_metadata( &ad);
rbuf += sizeof( id );
*rbuflen = 2 * sizeof( id );
break;
- case UUID_LOCAL:
- free(name);
- return (AFPERR_NOITEM);
default:
return AFPERR_MISC;
}
#endif
#include <atalk/directory.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
/* directory bits */
#include <atalk/util.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "desktop.h"
#include "directory.h"
#include "dircache.h"
#include "volume.h"
-#include "globals.h"
#include "file.h"
#include "fork.h"
#include "filedir.h"
#include <atalk/afp.h>
#include <atalk/logger.h>
#include <atalk/ea.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "desktop.h"
#include "directory.h"
--- /dev/null
+/*\r
+ * Copyright (c) 2010 Mark Williams\r
+ *\r
+ * File change event API for netatalk\r
+ *\r
+ * for every detected filesystem change a UDP packet is sent to an arbitrary list\r
+ * of listeners. Each packet contains unix path of modified filesystem element,\r
+ * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending\r
+ * out packets synchronuosly as they are created by the afp functions. This should not affect\r
+ * performance measurably. The only delaying calls occur during initialization, if we have to\r
+ * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use\r
+ * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with\r
+ * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by\r
+ * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.\r
+ *\r
+ * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that\r
+ * the listener has lost at least one filesystem event\r
+ * \r
+ * All Rights Reserved. See COPYRIGHT.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif /* HAVE_CONFIG_H */\r
+\r
+#include <stdio.h>\r
+\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <errno.h>\r
+#include <time.h>\r
+\r
+\r
+#include <sys/param.h>\r
+#include <sys/socket.h>\r
+#include <netinet/in.h>\r
+#include <arpa/inet.h>\r
+#include <netdb.h>\r
+\r
+#include <netatalk/at.h>\r
+\r
+#include <atalk/adouble.h>\r
+#include <atalk/vfs.h>\r
+#include <atalk/logger.h>\r
+#include <atalk/afp.h>\r
+#include <atalk/util.h>\r
+#include <atalk/cnid.h>\r
+#include <atalk/unix.h>\r
+#include <atalk/fce_api.h>\r
+#include <atalk/globals.h>\r
+\r
+#include "fork.h"\r
+#include "file.h"\r
+#include "directory.h"\r
+#include "desktop.h"\r
+#include "volume.h"\r
+\r
+// ONLY USED IN THIS FILE\r
+#include "fce_api_internal.h"\r
+\r
+#define FCE_TRUE 1\r
+#define FCE_FALSE 0\r
+\r
+/* We store our connection data here */\r
+static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS];\r
+static int udp_sockets = 0;\r
+static int udp_initialized = FCE_FALSE;\r
+static unsigned long fce_ev_enabled =\r
+ (1 << FCE_FILE_MODIFY) |\r
+ (1 << FCE_FILE_DELETE) |\r
+ (1 << FCE_DIR_DELETE) |\r
+ (1 << FCE_FILE_CREATE) |\r
+ (1 << FCE_DIR_CREATE);\r
+\r
+static uint64_t tm_used; /* used for passing to event handler */\r
+#define MAXIOBUF 1024\r
+static char iobuf[MAXIOBUF];\r
+static const char *skip_files[] = \r
+{\r
+ ".DS_Store",\r
+ NULL\r
+};\r
+\r
+/*\r
+ *\r
+ * Initialize network structs for any listeners\r
+ * We dont give return code because all errors are handled internally (I hope..)\r
+ *\r
+ * */\r
+void fce_init_udp()\r
+{\r
+ int rv;\r
+ struct addrinfo hints, *servinfo, *p;\r
+\r
+ if (udp_initialized == FCE_TRUE)\r
+ return;\r
+\r
+ memset(&hints, 0, sizeof hints);\r
+ hints.ai_family = AF_UNSPEC;\r
+ hints.ai_socktype = SOCK_DGRAM;\r
+\r
+ for (int i = 0; i < udp_sockets; i++) {\r
+ struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+ /* Close any pending sockets */\r
+ if (udp_entry->sock != -1)\r
+ close(udp_entry->sock);\r
+\r
+ if ((rv = getaddrinfo(udp_entry->addr, udp_entry->port, &hints, &servinfo)) != 0) {\r
+ LOG(log_error, logtype_afpd, "fce_init_udp: getaddrinfo(%s:%s): %s",\r
+ udp_entry->addr, udp_entry->port, gai_strerror(rv));\r
+ continue;\r
+ }\r
+\r
+ /* loop through all the results and make a socket */\r
+ for (p = servinfo; p != NULL; p = p->ai_next) {\r
+ if ((udp_entry->sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {\r
+ LOG(log_error, logtype_afpd, "fce_init_udp: socket(%s:%s): %s",\r
+ udp_entry->addr, udp_entry->port, strerror(errno));\r
+ continue;\r
+ }\r
+ break;\r
+ }\r
+\r
+ if (p == NULL) {\r
+ LOG(log_error, logtype_afpd, "fce_init_udp: no socket for %s:%s",\r
+ udp_entry->addr, udp_entry->port);\r
+ }\r
+ udp_entry->addrinfo = *p;\r
+ memcpy(&udp_entry->addrinfo, p, sizeof(struct addrinfo));\r
+ memcpy(&udp_entry->sockaddr, p->ai_addr, sizeof(struct sockaddr_storage));\r
+ freeaddrinfo(servinfo);\r
+ }\r
+\r
+ udp_initialized = FCE_TRUE;\r
+}\r
+\r
+void fce_cleanup()\r
+{\r
+ if (udp_initialized == FCE_FALSE )\r
+ return;\r
+\r
+ for (int i = 0; i < udp_sockets; i++)\r
+ {\r
+ struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+ /* Close any pending sockets */\r
+ if (udp_entry->sock != -1)\r
+ {\r
+ close( udp_entry->sock );\r
+ udp_entry->sock = -1;\r
+ }\r
+ }\r
+ udp_initialized = FCE_FALSE;\r
+}\r
+\r
+\r
+/*\r
+ * Construct a UDP packet for our listeners and return packet size\r
+ * */\r
+static ssize_t build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id )\r
+{\r
+ size_t pathlen;\r
+ ssize_t data_len = 0;\r
+\r
+ strncpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) );\r
+ packet->version = FCE_PACKET_VERSION;\r
+ packet->mode = mode;\r
+ packet->event_id = event_id;\r
+\r
+ pathlen = strlen(path) + 1; /* include string terminator */\r
+\r
+ /* This should never happen, but before we bust this server, we send nonsense, fce listener has to cope */\r
+ if (pathlen >= MAXPATHLEN)\r
+ pathlen = MAXPATHLEN - 1;\r
+\r
+ /* This is the payload len. Means: the stream has len bytes more until packet is finished */\r
+ /* A server should read the first 16 byte, decode them and then fetch the rest */\r
+ data_len = FCE_PACKET_HEADER_SIZE + pathlen;\r
+ packet->datalen = pathlen;\r
+\r
+ switch (mode) {\r
+ case FCE_TM_SIZE:\r
+ tm_used = hton64(tm_used);\r
+ memcpy(packet->data, &tm_used, sizeof(tm_used));\r
+ strncpy(packet->data + sizeof(tm_used), path, pathlen);\r
+\r
+ packet->datalen += sizeof(tm_used);\r
+ data_len += sizeof(tm_used);\r
+ break;\r
+ default:\r
+ strncpy(packet->data, path, pathlen);\r
+ break;\r
+ }\r
+\r
+ /* return the packet len */\r
+ return data_len;\r
+}\r
+\r
+static int pack_fce_packet(struct fce_packet *packet, unsigned char *buf)\r
+{\r
+ unsigned char *p = buf;\r
+\r
+ memcpy(p, &packet->magic[0], sizeof(packet->magic));\r
+ p += sizeof(packet->magic);\r
+\r
+ *p = packet->version;\r
+ p++;\r
+ \r
+ *p = packet->mode;\r
+ p++;\r
+ \r
+ uint32_t id = htonl(packet->event_id);\r
+ memcpy(p, &id, sizeof(id));\r
+ p += sizeof(packet->event_id);\r
+\r
+ uint16_t l = htons(packet->datalen);\r
+ memcpy(p, &l, sizeof(l));\r
+ p += sizeof(l);\r
+\r
+ memcpy(p, &packet->data[0], packet->datalen);\r
+ p += packet->datalen;\r
+\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * Send the fce information to all (connected) listeners\r
+ * We dont give return code because all errors are handled internally (I hope..)\r
+ * */\r
+static void send_fce_event( char *path, int mode )\r
+{ \r
+ struct fce_packet packet;\r
+ void *data = &packet;\r
+ static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */\r
+\r
+ LOG(log_debug, logtype_afpd, "send_fce_event: start");\r
+\r
+ time_t now = time(NULL);\r
+\r
+ /* build our data packet */\r
+ ssize_t data_len = build_fce_packet( &packet, path, mode, ++event_id );\r
+ pack_fce_packet(&packet, iobuf);\r
+\r
+ for (int i = 0; i < udp_sockets; i++)\r
+ {\r
+ int sent_data = 0;\r
+ struct udp_entry *udp_entry = udp_socket_list + i;\r
+\r
+ /* we had a problem earlier ? */\r
+ if (udp_entry->sock == -1)\r
+ {\r
+ /* We still have to wait ?*/\r
+ if (now < udp_entry->next_try_on_error)\r
+ continue;\r
+\r
+ /* Reopen socket */\r
+ udp_entry->sock = socket(udp_entry->addrinfo.ai_family,\r
+ udp_entry->addrinfo.ai_socktype,\r
+ udp_entry->addrinfo.ai_protocol);\r
+ \r
+ if (udp_entry->sock == -1) {\r
+ /* failed again, so go to rest again */\r
+ LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno );\r
+\r
+ udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;\r
+ continue;\r
+ }\r
+\r
+ udp_entry->next_try_on_error = 0;\r
+\r
+ /* Okay, we have a running socket again, send server that we had a problem on our side*/\r
+ data_len = build_fce_packet( &packet, "", FCE_CONN_BROKEN, 0 );\r
+ pack_fce_packet(&packet, iobuf);\r
+\r
+ sendto(udp_entry->sock,\r
+ iobuf,\r
+ data_len,\r
+ 0,\r
+ (struct sockaddr *)&udp_entry->sockaddr,\r
+ udp_entry->addrinfo.ai_addrlen);\r
+\r
+ /* Rebuild our original data packet */\r
+ data_len = build_fce_packet( &packet, path, mode, event_id );\r
+ pack_fce_packet(&packet, iobuf);\r
+ }\r
+\r
+ sent_data = sendto(udp_entry->sock,\r
+ iobuf,\r
+ data_len,\r
+ 0,\r
+ (struct sockaddr *)&udp_entry->sockaddr,\r
+ udp_entry->addrinfo.ai_addrlen);\r
+\r
+ /* Problems ? */\r
+ if (sent_data != data_len) {\r
+ /* Argh, socket broke, we close and retry later */\r
+ LOG(log_error, logtype_afpd, "send_fce_event: error sending packet to %s:%s, transfered %d of %d: %s",\r
+ udp_entry->addr, udp_entry->port, sent_data, data_len, strerror(errno));\r
+\r
+ close( udp_entry->sock );\r
+ udp_entry->sock = -1;\r
+ udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S;\r
+ }\r
+ }\r
+}\r
+\r
+static int add_udp_socket(const char *target_ip, const char *target_port )\r
+{\r
+ if (target_port == NULL)\r
+ target_port = FCE_DEFAULT_PORT_STRING;\r
+\r
+ if (udp_sockets >= FCE_MAX_UDP_SOCKS) {\r
+ LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS );\r
+ return AFPERR_PARAM;\r
+ }\r
+\r
+ udp_socket_list[udp_sockets].addr = strdup(target_ip);\r
+ udp_socket_list[udp_sockets].port = strdup(target_port);\r
+ udp_socket_list[udp_sockets].sock = -1;\r
+ memset(&udp_socket_list[udp_sockets].addrinfo, 0, sizeof(struct addrinfo));\r
+ memset(&udp_socket_list[udp_sockets].sockaddr, 0, sizeof(struct sockaddr_storage));\r
+ udp_socket_list[udp_sockets].next_try_on_error = 0;\r
+\r
+ udp_sockets++;\r
+\r
+ return AFP_OK;\r
+}\r
+\r
+/*\r
+ *\r
+ * Dispatcher for all incoming file change events\r
+ *\r
+ * */\r
+static int register_fce(const char *u_name, int is_dir, int mode)\r
+{\r
+ if (udp_sockets == 0)\r
+ /* No listeners configured */\r
+ return AFP_OK;\r
+\r
+ if (u_name == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ static int first_event = FCE_TRUE;\r
+\r
+ /* do some initialization on the fly the first time */\r
+ if (first_event) {\r
+ fce_initialize_history();\r
+ }\r
+\r
+ /* handle files which should not cause events (.DS_Store atc. ) */\r
+ for (int i = 0; skip_files[i] != NULL; i++)\r
+ {\r
+ if (!strcmp( u_name, skip_files[i]))\r
+ return AFP_OK;\r
+ }\r
+\r
+\r
+ char full_path_buffer[MAXPATHLEN + 1] = {""};\r
+ const char *cwd = getcwdpath();\r
+\r
+ if (mode == FCE_TM_SIZE) {\r
+ strlcpy(full_path_buffer, u_name, MAXPATHLEN);\r
+ } else if (!is_dir || mode == FCE_DIR_DELETE) {\r
+ if (strlen( cwd ) + strlen( u_name) + 1 >= MAXPATHLEN) {\r
+ LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name );\r
+ return AFPERR_PARAM;\r
+ }\r
+ sprintf( full_path_buffer, "%s/%s", cwd, u_name );\r
+ } else {\r
+ if (strlen( cwd ) >= MAXPATHLEN) {\r
+ LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd);\r
+ return AFPERR_PARAM;\r
+ }\r
+ strcpy( full_path_buffer, cwd);\r
+ }\r
+\r
+ /* Can we ignore this event based on type or history? */\r
+ if (!(mode & FCE_TM_SIZE) && fce_handle_coalescation( full_path_buffer, is_dir, mode ))\r
+ {\r
+ LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer );\r
+ return AFP_OK;\r
+ }\r
+\r
+ LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer );\r
+\r
+\r
+ /* we do initilization on the fly, no blocking calls in here \r
+ * (except when using FQDN in broken DNS environment)\r
+ */\r
+ if (first_event == FCE_TRUE)\r
+ {\r
+ fce_init_udp();\r
+ \r
+ /* Notify listeners the we start from the beginning */\r
+ send_fce_event( "", FCE_CONN_START );\r
+ \r
+ first_event = FCE_FALSE;\r
+ }\r
+\r
+ /* Handle UDP transport */\r
+ send_fce_event( full_path_buffer, mode );\r
+\r
+ return AFP_OK;\r
+}\r
+\r
+\r
+/******************** External calls start here **************************/\r
+\r
+/*\r
+ * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c)\r
+ * */\r
+#ifndef FCE_TEST_MAIN\r
+\r
+int fce_register_delete_file( struct path *path )\r
+{\r
+ int ret = AFP_OK;\r
+\r
+ if (path == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_FILE_DELETE)))\r
+ return ret;\r
+ \r
+ ret = register_fce( path->u_name, FALSE, FCE_FILE_DELETE );\r
+\r
+ return ret;\r
+}\r
+int fce_register_delete_dir( char *name )\r
+{\r
+ int ret = AFP_OK;\r
+\r
+ if (name == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_DIR_DELETE)))\r
+ return ret;\r
+ \r
+ ret = register_fce( name, TRUE, FCE_DIR_DELETE);\r
+\r
+ return ret;\r
+}\r
+\r
+int fce_register_new_dir( struct path *path )\r
+{\r
+ int ret = AFP_OK;\r
+\r
+ if (path == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_DIR_CREATE)))\r
+ return ret;\r
+\r
+ ret = register_fce( path->u_name, TRUE, FCE_DIR_CREATE );\r
+\r
+ return ret;\r
+}\r
+\r
+\r
+int fce_register_new_file( struct path *path )\r
+{\r
+ int ret = AFP_OK;\r
+\r
+ if (path == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_FILE_CREATE)))\r
+ return ret;\r
+\r
+ ret = register_fce( path->u_name, FALSE, FCE_FILE_CREATE );\r
+\r
+ return ret;\r
+}\r
+\r
+int fce_register_file_modification( struct ofork *ofork )\r
+{\r
+ char *u_name = NULL;\r
+ struct vol *vol;\r
+ int ret = AFP_OK;\r
+\r
+ if (ofork == NULL || ofork->of_vol == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_FILE_MODIFY)))\r
+ return ret;\r
+\r
+ vol = ofork->of_vol;\r
+\r
+ if (NULL == (u_name = mtoupath(vol, of_name(ofork), ofork->of_did, utf8_encoding()))) \r
+ {\r
+ return AFPERR_MISC;\r
+ }\r
+ \r
+ ret = register_fce( u_name, FALSE, FCE_FILE_MODIFY );\r
+ \r
+ return ret; \r
+}\r
+\r
+int fce_register_tm_size(const char *vol, size_t used)\r
+{\r
+ int ret = AFP_OK;\r
+\r
+ if (vol == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ if (!(fce_ev_enabled & (1 << FCE_TM_SIZE)))\r
+ return ret;\r
+\r
+ tm_used = used; /* oh what a hack */\r
+ ret = register_fce(vol, FALSE, FCE_TM_SIZE);\r
+\r
+ return ret;\r
+}\r
+#endif\r
+\r
+/*\r
+ *\r
+ * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times)\r
+ *\r
+ * */\r
+int fce_add_udp_socket(const char *target)\r
+{\r
+ const char *port = FCE_DEFAULT_PORT_STRING;\r
+ char target_ip[256] = {""};\r
+\r
+ strncpy(target_ip, target, sizeof(target_ip) -1);\r
+\r
+ char *port_delim = strchr( target_ip, ':' );\r
+ if (port_delim) {\r
+ *port_delim = 0;\r
+ port = port_delim + 1;\r
+ }\r
+ return add_udp_socket(target_ip, port);\r
+}\r
+\r
+int fce_set_events(const char *events)\r
+{\r
+ char *e;\r
+ char *p;\r
+ \r
+ if (events == NULL)\r
+ return AFPERR_PARAM;\r
+\r
+ e = strdup(events);\r
+\r
+ fce_ev_enabled = 0;\r
+\r
+ for (p = strtok(e, ","); p; p = strtok(NULL, ",")) {\r
+ if (strcmp(p, "fmod") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_FILE_MODIFY);\r
+ } else if (strcmp(p, "fdel") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_FILE_DELETE);\r
+ } else if (strcmp(p, "ddel") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_DIR_DELETE);\r
+ } else if (strcmp(p, "fcre") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_FILE_CREATE);\r
+ } else if (strcmp(p, "dcre") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_DIR_CREATE);\r
+ } else if (strcmp(p, "tmsz") == 0) {\r
+ fce_ev_enabled |= (1 << FCE_TM_SIZE);\r
+ }\r
+ }\r
+\r
+ free(e);\r
+}\r
+\r
+#ifdef FCE_TEST_MAIN\r
+\r
+\r
+void shortsleep( unsigned int us )\r
+{ \r
+ usleep( us );\r
+}\r
+int main( int argc, char*argv[] )\r
+{\r
+ int c,ret;\r
+\r
+ char *port = FCE_DEFAULT_PORT_STRING;\r
+ char *host = "localhost";\r
+ int delay_between_events = 1000;\r
+ int event_code = FCE_FILE_MODIFY;\r
+ char pathbuff[1024];\r
+ int duration_in_seconds = 0; // TILL ETERNITY\r
+ char target[256];\r
+ char *path = getcwd( pathbuff, sizeof(pathbuff) );\r
+\r
+ // FULLSPEED TEST IS "-s 1001" -> delay is 0 -> send packets without pause\r
+\r
+ while ((c = getopt(argc, argv, "d:e:h:p:P:s:")) != -1) {\r
+ switch(c) {\r
+ case '?':\r
+ fprintf(stdout, "%s: [ -p Port -h Listener1 [ -h Listener2 ...] -P path -s Delay_between_events_in_us -e event_code -d Duration ]\n", argv[0]);\r
+ exit(1);\r
+ break;\r
+ case 'd':\r
+ duration_in_seconds = atoi(optarg);\r
+ break;\r
+ case 'e':\r
+ event_code = atoi(optarg);\r
+ break;\r
+ case 'h':\r
+ host = strdup(optarg);\r
+ break;\r
+ case 'p':\r
+ port = strdup(optarg);\r
+ break;\r
+ case 'P':\r
+ path = strdup(optarg);\r
+ break;\r
+ case 's':\r
+ delay_between_events = atoi(optarg);\r
+ break;\r
+ }\r
+ }\r
+\r
+ sprintf(target, "%s:%s", host, port);\r
+ if (fce_add_udp_socket(target) != 0)\r
+ return 1;\r
+\r
+ int ev_cnt = 0;\r
+ time_t start_time = time(NULL);\r
+ time_t end_time = 0;\r
+\r
+ if (duration_in_seconds)\r
+ end_time = start_time + duration_in_seconds;\r
+\r
+ while (1)\r
+ {\r
+ time_t now = time(NULL);\r
+ if (now > start_time)\r
+ {\r
+ start_time = now;\r
+ fprintf( stdout, "%d events/s\n", ev_cnt );\r
+ ev_cnt = 0;\r
+ }\r
+ if (end_time && now >= end_time)\r
+ break;\r
+\r
+ register_fce( path, 0, event_code );\r
+ ev_cnt++;\r
+\r
+ \r
+ shortsleep( delay_between_events );\r
+ }\r
+}\r
+#endif /* TESTMAIN*/\r
--- /dev/null
+/* \r
+ * File: fce_api_internal.h\r
+ * Author: mw\r
+ *\r
+ * Created on 1. Oktober 2010, 23:48\r
+ */\r
+\r
+#ifndef _FCE_API_INTERNAL_H\r
+#define _FCE_API_INTERNAL_H\r
+\r
+#define FCE_MAX_UDP_SOCKS 5 /* Allow a maximum of udp listeners for file change events */\r
+#define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */\r
+#define FCE_PACKET_VERSION 1\r
+#define FCE_HISTORY_LEN 10 /* This is used to coalesce events */\r
+#define MAX_COALESCE_TIME_MS 1000 /* Events oldeer than this are not coalesced */\r
+\r
+struct udp_entry\r
+{\r
+ int sock;\r
+ char *addr;\r
+ char *port;\r
+ struct addrinfo addrinfo;\r
+ struct sockaddr_storage sockaddr;\r
+ time_t next_try_on_error; /* In case of error set next timestamp to retry */\r
+};\r
+\r
+struct fce_history\r
+{\r
+ unsigned char mode;\r
+ int is_dir;\r
+ char path[MAXPATHLEN + 1];\r
+ struct timeval tv;\r
+};\r
+\r
+#define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN)\r
+\r
+int fce_handle_coalescation( char *path, int is_dir, int mode );\r
+void fce_initialize_history();\r
+\r
+\r
+#endif /* _FCE_API_INTERNAL_H */\r
+\r
--- /dev/null
+/*\r
+ * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $\r
+ *\r
+ * Copyright (c) 2010 Mark Williams\r
+ *\r
+ * File change event API for netatalk\r
+ *\r
+ * for every detected filesystem change a UDP packet is sent to an arbitrary list\r
+ * of listeners. Each packet contains unix path of modified filesystem element,\r
+ * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending\r
+ * out packets synchronuosly as they are created by the afp functions. This should not affect\r
+ * performance measurably. The only delaying calls occur during initialization, if we have to\r
+ * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use\r
+ * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with\r
+ * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by\r
+ * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on.\r
+ *\r
+ * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that\r
+ * the listener has lost at least one filesystem event\r
+ * \r
+ * All Rights Reserved. See COPYRIGHT.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif /* HAVE_CONFIG_H */\r
+\r
+#include <stdio.h>\r
+\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <errno.h>\r
+#include <time.h>\r
+\r
+\r
+#include <sys/param.h>\r
+#include <sys/socket.h>\r
+#include <netinet/in.h>\r
+#include <arpa/inet.h>\r
+#include <netdb.h>\r
+\r
+#include <netatalk/at.h>\r
+\r
+#include <atalk/adouble.h>\r
+#include <atalk/vfs.h>\r
+#include <atalk/logger.h>\r
+#include <atalk/afp.h>\r
+#include <atalk/util.h>\r
+#include <atalk/cnid.h>\r
+#include <atalk/unix.h>\r
+#include <atalk/fce_api.h>\r
+#include <atalk/globals.h>\r
+\r
+#include "fork.h"\r
+#include "file.h"\r
+#include "directory.h"\r
+#include "desktop.h"\r
+#include "volume.h"\r
+\r
+// ONLY USED IN THIS FILE\r
+#include "fce_api_internal.h"\r
+\r
+#define FCE_TRUE 1\r
+#define FCE_FALSE 0\r
+\r
+/* We store our connection data here */\r
+static char coalesce[80] = {""};\r
+static struct fce_history fce_history_list[FCE_HISTORY_LEN];\r
+\r
+\r
+\r
+\r
+/****\r
+* With coalesce we try to reduce the events over UDP, the eventlistener would throw these \r
+* events away anyway.\r
+* This works only, if the connected listener uses the events on a "per directory" base\r
+* It is a very simple aproach, but saves a lot of events sent to listeners.\r
+* Every "child element" event is ignored as long as its parent event is not older \r
+* than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, \r
+* this probably will not work recursive, because the time to copy data will exceed this \r
+* event timeout. \r
+* \r
+****/\r
+static int coalesce_none()\r
+{\r
+ return coalesce[0] == 0;\r
+}\r
+static int coalesce_all()\r
+{\r
+ return !strcmp( coalesce, "all" );\r
+}\r
+static int coalesce_create()\r
+{\r
+ return !strcmp( coalesce, "create" ) || coalesce_all();\r
+}\r
+static int coalesce_delete()\r
+{\r
+ return !strcmp( coalesce, "delete" ) || coalesce_all();\r
+}\r
+\r
+void fce_initialize_history()\r
+{\r
+ for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
+ {\r
+ memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) );\r
+ }\r
+}\r
+\r
+static long get_ms_difftime ( struct timeval *tv1, struct timeval *tv2 )\r
+{\r
+ unsigned long s = tv2->tv_sec - tv1->tv_sec;\r
+ long us = tv2->tv_usec - tv1->tv_usec;\r
+\r
+ return s * 1000 + us/1000;\r
+}\r
+\r
+int fce_handle_coalescation( char *path, int is_dir, int mode )\r
+{\r
+ if (coalesce_none())\r
+ return FALSE;\r
+\r
+ \r
+\r
+ // First one:\r
+ // After a file creation *ALWAYS* a file modification is produced\r
+ if (mode == FCE_FILE_CREATE)\r
+ {\r
+ if (coalesce_create())\r
+ {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ /* get timestamp */\r
+ struct timeval tv;\r
+ gettimeofday(&tv, 0);\r
+\r
+\r
+ /* These two are used to eval our next index in history */\r
+ /* the history is unsorted, speed should not be a problem, length is 10 */\r
+ unsigned long oldest_entry = (unsigned long )((long)-1);\r
+ int oldest_entry_idx = -1;\r
+\r
+ /* Now detect events in the very near history */\r
+ for (int i = 0; i < FCE_HISTORY_LEN; i++)\r
+ {\r
+ struct fce_history *fh = &fce_history_list[i];\r
+\r
+ //* Not inited ? */\r
+ if (fh->tv.tv_sec == 0)\r
+ {\r
+ /* we can use it for new elements */\r
+ oldest_entry = 0;\r
+ oldest_entry_idx = i;\r
+ continue;\r
+ }\r
+\r
+ //* Too old ? */\r
+ if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS)\r
+ {\r
+ /* Invalidate entry */\r
+ fh->tv.tv_sec = 0;\r
+\r
+ oldest_entry = 0;\r
+ oldest_entry_idx = i; \r
+ continue;\r
+ }\r
+\r
+\r
+ /* If we find a parent dir wich was created we are done */\r
+ if (coalesce_create() && fh->mode == FCE_DIR_CREATE)\r
+ {\r
+ //* Parent dir ? */\r
+ if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
+ {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ /* If we find a parent dir we should be DELETED we are done */\r
+ if (coalesce_delete() && fh->is_dir && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE))\r
+ {\r
+ //* Parent dir ? */\r
+ if (!strncmp( fh->path, path, strlen( fh->path ) ) )\r
+ {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ //* Detect oldest entry for next new entry */\r
+ if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry)\r
+ {\r
+ oldest_entry = fh->tv.tv_sec;\r
+ oldest_entry_idx = i;\r
+ }\r
+ }\r
+\r
+ /* We have a new entry for the history, register it */\r
+ fce_history_list[oldest_entry_idx].tv = tv;\r
+ fce_history_list[oldest_entry_idx].mode = mode;\r
+ fce_history_list[oldest_entry_idx].is_dir = is_dir;\r
+ strncpy( fce_history_list[oldest_entry_idx].path, path, MAXPATHLEN);\r
+\r
+ /* we have to handle this event */\r
+ return FALSE;\r
+}\r
+\r
+/*\r
+ *\r
+ * Set event coalescation to reduce number of events sent over UDP \r
+ * all|delete|create\r
+ *\r
+ *\r
+ * */\r
+\r
+int fce_set_coalesce( char *coalesce_opt )\r
+{\r
+ strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 ); \r
+}\r
+\r
+\r
+\r
+\r
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/unix.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#include "directory.h"
#include "dircache.h"
#include "fork.h"
#include "file.h"
#include "filedir.h"
-#include "globals.h"
#include "unix.h"
/* the format for the finderinfo fields (from IM: Toolbox Essentials):
ad_flush(&ad);
ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
+ fce_register_new_file(s_path);
createfile_done:
curdir->d_offcnt++;
#include <sys/param.h>
#include <arpa/inet.h>
#include <atalk/adouble.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
#include <atalk/acl.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#include "directory.h"
#include "dircache.h"
#include "volume.h"
#include "fork.h"
#include "file.h"
-#include "globals.h"
#include "filedir.h"
#include "unix.h"
upath = s_path->u_name;
if ( path_isadir( s_path) ) {
- if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
+ if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT) {
rc = AFPERR_ACCESS;
- else
- rc = deletecurdir( vol);
+ } else {
+ /* we have to cache this, the structs are lost in deletcurdir*/
+ /* but we need the positive returncode to send our event */
+ bstring dname;
+ if ((dname = bstrcpy(curdir->d_u_name)) == NULL)
+ return AFPERR_MISC;
+ if ((rc = deletecurdir(vol)) == AFP_OK)
+ fce_register_delete_dir(cfrombstr(dname));
+ bdestroy(dname);
+ }
} else if (of_findname(s_path)) {
rc = AFPERR_BUSY;
} else {
*/
if (s_path->st_valid && s_path->st_errno == ENOENT) {
rc = AFPERR_NOOBJ;
- }
- else {
- rc = deletefile(vol, -1, upath, 1);
-
+ } else {
+ if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK) {
+ fce_register_delete_file( s_path );
+ if (vol->v_tm_used < s_path->st.st_size)
+ vol->v_tm_used = 0;
+ else
+ vol->v_tm_used -= s_path->st.st_size;
+ }
struct dir *cachedfile;
if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath)))) {
dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
#define AFPD_FILEDIR_H 1
#include <sys/stat.h>
-#include "globals.h"
+#include <atalk/globals.h>
#include "volume.h"
extern struct afp_options default_options;
#include <sys/socket.h>
#include <inttypes.h>
-#include <netatalk/at.h>
#include <atalk/dsi.h>
#include <atalk/afp.h>
#include <atalk/adouble.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "fork.h"
#include "file.h"
-#include "globals.h"
#include "directory.h"
#include "desktop.h"
#include "volume.h"
if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
ofork->of_flags |= AFPFORK_DIRTY;
+ /* we have modified any fork, remember until close_fork */
+ ofork->of_flags |= AFPFORK_MODIFIED;
+
+ /* update write count */
+ ofork->of_vol->v_written += reqcount;
+
*rbuflen = set_off_t (offset, rbuf, is64);
return( AFP_OK );
#define AFPFORK_ACCRD (1<<4)
#define AFPFORK_ACCWR (1<<5)
#define AFPFORK_ACCMASK (AFPFORK_ACCRD | AFPFORK_ACCWR)
+#define AFPFORK_MODIFIED (1<<6) /* used in FCE for modified files */
#ifdef AFS
extern struct ofork *writtenfork;
#include <ctype.h>
#include <pwd.h>
-#include "globals.h"
+#include <atalk/globals.h>
static char *l_curr;
static char *l_end;
+++ /dev/null
-/*
- * Copyright (c) 1990,1993 Regents of The University of Michigan.
- * All Rights Reserved. See COPYRIGHT.
- */
-
-#ifndef AFPD_GLOBALS_H
-#define AFPD_GLOBALS_H 1
-
-#include <sys/param.h>
-
-#ifdef ADMIN_GRP
-#include <grp.h>
-#include <sys/types.h>
-#endif /* ADMIN_GRP */
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h> /* this isn't header-protected under ultrix */
-#endif /* HAVE_NETDB_H */
-
-#include <netatalk/at.h>
-#include <atalk/afp.h>
-#include <atalk/compat.h>
-#include <atalk/unicode.h>
-#include <atalk/uam.h>
-
-/* #define DOSFILELEN 12 */ /* Type1, DOS-compat*/
-#define MACFILELEN 31 /* Type2, HFS-compat */
-#define UTF8FILELEN_EARLY 255 /* Type3, early Mac OS X 10.0-10.4.? */
-/* #define UTF8FILELEN_NAME_MAX 765 */ /* Type3, 10.4.?- , getconf NAME_MAX */
-/* #define UTF8FILELEN_SPEC 0xFFFF */ /* Type3, spec on document */
-/* #define HFSPLUSFILELEN 510 */ /* HFS+ spec, 510byte = 255codepoint */
-
-#define MAXUSERLEN 256
-
-#define OPTION_DEBUG (1 << 0)
-#define OPTION_USERVOLFIRST (1 << 1)
-#define OPTION_NOUSERVOL (1 << 2)
-#define OPTION_PROXY (1 << 3)
-#define OPTION_CUSTOMICON (1 << 4)
-#define OPTION_NOSLP (1 << 5)
-#define OPTION_ANNOUNCESSH (1 << 6)
-#define OPTION_UUID (1 << 7)
-#define OPTION_ACL2MACCESS (1 << 8)
-#define OPTION_NOZEROCONF (1 << 9)
-
-/* a couple of these options could get stuck in unions to save
- * space. */
-struct afp_volume_name {
- time_t mtime;
- char *name;
- char *full_name;
- int loaded;
-};
-
-struct afp_options {
- int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
- int sleep; /* Maximum time allowed to sleep (in tickles) */
- int disconnected; /* Maximum time in disconnected state (in tickles) */
- unsigned int tcp_sndbuf, tcp_rcvbuf;
- unsigned char passwdbits, passwdminlen, loginmaxfail;
- u_int32_t server_quantum;
- int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
- char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
- char *uampath, *fqdn;
- char *pidfile;
- char *sigconffile;
- char *uuidconf;
- struct afp_volume_name defaultvol, systemvol, uservol;
- int closevol;
-
- char *guest, *loginmesg, *keyfile, *passwdfile;
- char *uamlist;
- char *authprintdir;
- char *signatureopt;
- unsigned char signature[16];
- char *k5service, *k5realm, *k5keytab;
- char *unixcodepage,*maccodepage;
- charset_t maccharset, unixcharset;
- mode_t umask;
- mode_t save_mask;
-#ifdef ADMIN_GRP
- gid_t admingid;
-#endif /* ADMIN_GRP */
- int volnamelen;
-
- /* default value for winbind authentication */
- char *ntdomain, *ntseparator;
- char *logconfig;
-};
-
-#define AFPOBJ_TMPSIZ (MAXPATHLEN)
-typedef struct _AFPObj {
- int proto;
- unsigned long servernum;
- void *handle; /* either (DSI *) or (ASP *) */
- void *config;
- struct afp_options options;
- char *Obj, *Type, *Zone;
- char username[MAXUSERLEN];
- void (*logout)(void), (*exit)(int);
- int (*reply)(void *, int);
- int (*attention)(void *, AFPUserBytes);
- /* to prevent confusion, only use these in afp_* calls */
- char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
- void *uam_cookie; /* cookie for uams */
- struct session_info sinfo;
- uid_t uid; /* client running user id */
- int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
-} AFPObj;
-
-/* typedef for AFP functions handlers */
-typedef int (*AFPCmd)(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
-
-/* afp_dsi.c */
-extern AFPObj *AFPobj;
-
-extern int afp_version;
-extern int afp_errno;
-extern unsigned char nologin;
-extern struct dir *curdir;
-extern char getwdbuf[];
-
-/* FIXME CNID */
-extern const char *Cnid_srv;
-extern const char *Cnid_port;
-
-extern int get_afp_errno (const int param);
-extern void afp_options_init (struct afp_options *);
-extern int afp_options_parse (int, char **, struct afp_options *);
-extern int afp_options_parseline (char *, struct afp_options *);
-extern void afp_options_free (struct afp_options *,
- const struct afp_options *);
-extern void setmessage (const char *);
-extern void readmessage (AFPObj *);
-
-/* gettok.c */
-extern void initline (int, char *);
-extern int parseline (int, char *);
-
-/* afp_util.c */
-extern const char *AfpNum2name (int );
-extern const char *AfpErr2name(int err);
-
-/* directory.c */
-extern struct dir rootParent;
-
-extern void afp_over_dsi (AFPObj *);
-
-#endif /* globals.h */
#ifndef AFPD_ICON_H
#define AFPD_ICON_H 1
-#include "globals.h"
+#include <atalk/globals.h>
static const unsigned char apple_atalk_icon[] = { /* default appletalk icon */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#include <sys/poll.h>
#include <errno.h>
#include <sys/wait.h>
+#include <sys/resource.h>
#include <atalk/logger.h>
#include <atalk/adouble.h>
-#include <netatalk/at.h>
#include <atalk/compat.h>
#include <atalk/dsi.h>
#include <atalk/afp.h>
#include <atalk/server_ipc.h>
#include <atalk/errchk.h>
#include <atalk/locking.h>
+#include <atalk/globals.h>
#include "event2/event.h"
#include "event2/http.h"
#include "event2/rpc.h"
-#include "globals.h"
#include "afp_config.h"
#include "status.h"
#include "fork.h"
static struct polldata *polldata;
static int fdset_size; /* current allocated size */
static int fdset_used; /* number of used elements */
-
+static int disasociated_ipc_fd; /* disasociated sessions uses this fd for IPC */
#ifdef TRU64
void afp_get_cmdline( int *ac, char ***av)
continue;
fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
}
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd, DISASOCIATED_IPC_FD, NULL);
}
static void fd_reset_listening_sockets(void)
continue;
fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
}
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd);
}
/* ------------------ */
static void afp_goaway(int sig)
{
- switch( sig ) {
+ AFPConfig *config;
- case SIGTERM :
- LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
- AFPConfig *config;
+ switch( sig ) {
+ case SIGTERM:
+ case SIGQUIT:
+ switch (sig) {
+ case SIGTERM:
+ LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
+ break;
+ case SIGQUIT:
+ LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT, NOT disconnecting clients");
+ break;
+ }
if (server_children)
server_child_kill(server_children, CHILD_DSIFORK, sig);
}
}
+static int setlimits(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) {
+ rlim.rlim_cur = 65535;
+ if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535)
+ rlim.rlim_max = 65535;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
int main(int ac, char **av)
{
AFPConfig *config;
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGUSR1);
-
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGUSR1);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGUSR1);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
exit(EXITERR_SYS);
}
+ sigemptyset( &sv.sa_mask );
+ sigaddset(&sv.sa_mask, SIGALRM);
+ sigaddset(&sv.sa_mask, SIGHUP);
+ sigaddset(&sv.sa_mask, SIGUSR1);
+ sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGTERM);
+ sv.sa_flags = SA_RESTART;
+ if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) {
+ LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+ exit(EXITERR_SYS);
+ }
+
/* afpd.conf: not in config file: lockfile, connections, configfile
* preference: command-line provides defaults.
* config file over-writes defaults.
#endif
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
+ disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
fd_set_listening_sockets();
+ /* set limits */
+ (void)setlimits();
+
afp_child_t *child;
+ int fd[2]; /* we only use one, but server_child_add expects [2] */
+ pid_t pid;
/* wait for an appleshare connection. parent remains in the loop
* while the children get handled by afp_over_{asp,dsi}. this is
}
for (int i = 0; i < fdset_used; i++) {
- if (fdset[i].revents & POLLIN) {
+ if (fdset[i].revents & (POLLIN | POLLERR | POLLHUP)) {
switch (polldata[i].fdtype) {
+
case LISTEN_FD:
config = (AFPConfig *)polldata[i].data;
/* config->server_start is afp_config.c:dsi_start() for DSI */
fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0], IPC_FD, child);
}
break;
+
case IPC_FD:
child = (afp_child_t *)polldata[i].data;
LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
+
if ((ret = ipc_server_read(server_children, child->ipc_fds[0])) == 0) {
fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0]);
close(child->ipc_fds[0]);
child->ipc_fds[0] = -1;
+ if (child->disasociated) {
+ LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
+ server_child_remove(server_children, CHILD_DSIFORK, child->pid);
+ }
}
break;
+
+ case DISASOCIATED_IPC_FD:
+ LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
+ if ((fd[0] = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
+ LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
+ break;
+ }
+ if (readt(fd[0], &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
+ LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
+ close(fd[0]);
+ }
+ LOG(log_note, logtype_afpd, "main: IPC reconnect from [%u]", pid);
+ if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, fd)) == NULL) {
+ LOG(log_error, logtype_afpd, "main: server_child_add");
+ close(fd[0]);
+ break;
+ }
+ child->disasociated = 1;
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd[0], IPC_FD, child);
+ break;
+
default:
LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
break;
#include <atalk/adouble.h>
#include <atalk/cnid.h>
#include <atalk/logger.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include <atalk/dsi.h>
#include <atalk/util.h>
#include <atalk/logger.h>
-#include "globals.h"
+#include <atalk/globals.h>
+
#include "misc.h"
#ifndef AFPD_MISC_H
#define AFPD_MISC_H 1
-#include "globals.h"
+#include <atalk/globals.h>
/* FP functions */
/* messages.c */
#include <atalk/util.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include "fork.h"
-/* we need to have a hashed list of oforks (by dev inode). just hash
- * by first letter. */
+/* we need to have a hashed list of oforks (by dev inode) */
#define OFORK_HASHSIZE 64
-static struct ofork *ofork_table[OFORK_HASHSIZE];
-
-static struct ofork **oforks = NULL;
+static struct ofork *ofork_table[OFORK_HASHSIZE]; /* forks hashed by dev/inode */
+static struct ofork **oforks = NULL; /* point to allocated table of open forks pointers */
static int nforks = 0;
static u_short lastrefnum = 0;
/* OR some of each character for the hash*/
static unsigned long hashfn(const struct file_key *key)
{
-#if 0
- unsigned long i = 0;
- while (*name) {
- i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
- }
-#endif
return key->inode & (OFORK_HASHSIZE - 1);
}
}
}
}
+
+ /* Somone has used write_fork, we assume file was changed, register it to file change event api */
+ if (ofork->of_flags & AFPFORK_MODIFIED) {
+ fce_register_file_modification(ofork);
+ }
+
ret = 0;
ad_unlock(ofork->of_ad, ofork->of_refnum);
#include <atalk/dsi.h>
#include <atalk/unicode.h>
#include <atalk/util.h>
+#include <atalk/globals.h>
-#include "globals.h" /* includes <netdb.h> */
#include "status.h"
#include "afp_config.h"
#include "icon.h"
#define AFPD_STATUS_H 1
#include <atalk/dsi.h>
-#include "globals.h"
+#include <atalk/globals.h>
+
#include "afp_config.h"
/* we use these to prevent whacky alignment problems */
#include <atalk/afp.h>
#include <atalk/uam.h>
-
-#include "globals.h"
+#include <atalk/globals.h>
/* grab the FP functions */
#include "auth.h"
#include <atalk/dsi.h>
#include <atalk/afp.h>
#include <atalk/util.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "afp_config.h"
#include "auth.h"
#include "uam_auth.h"
#include <pwd.h>
#include <atalk/uam.h>
-#include "globals.h"
+#include <atalk/globals.h>
struct uam_mod {
void *uam_module;
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <inttypes.h>
+#include <time.h>
#include <atalk/dsi.h>
#include <atalk/adouble.h>
#include <atalk/ea.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/ftw.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#ifdef CNID_DB
#include <atalk/cnid.h>
#endif /* CNID_DB*/
-#include "globals.h"
#include "directory.h"
#include "file.h"
#include "volume.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif /* ! MIN */
-#ifndef NO_LARGE_VOL_SUPPORT
-#if BYTE_ORDER == BIG_ENDIAN
-#define hton64(x) (x)
-#define ntoh64(x) (x)
-#else /* BYTE_ORDER == BIG_ENDIAN */
-#define hton64(x) ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
- (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
-#define ntoh64(x) (hton64(x))
-#endif /* BYTE_ORDER == BIG_ENDIAN */
-#endif /* ! NO_LARGE_VOL_SUPPORT */
-
#ifndef UUID_PRINTABLE_STRING_LENGTH
#define UUID_PRINTABLE_STRING_LENGTH 37
#endif
options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
else if (strcasecmp(p, "searchdb") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
-/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
-#if 0
- else if (strcasecmp(p, "cdrom") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_CDROM | AFPVOL_RO;
-#endif
+ else if (strcasecmp(p, "nonetids") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
p = strtok(NULL, ",");
}
p1->mtime = st.st_mtime;
}
- if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
- LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
- if ( fclose( fp ) != 0 ) {
- LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
+ /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
+ int retries = 2;
+ while (1) {
+ if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
+ retries--;
+ if (!retries) {
+ LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
+ if ( fclose( fp ) != 0 ) {
+ LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
+ }
+ return -1;
+ }
+ sleep(1);
+ continue;
}
- return -1;
+ break;
}
memset(save_options, 0, sizeof(save_options));
}
}
+static off_t getused_size; /* result of getused() */
+
+/*!
+ nftw callback for getused()
+ */
+static int getused_stat(const char *path,
+ const struct stat *statp,
+ int tflag,
+ struct FTW *ftw)
+{
+ off_t low, high;
+
+ if (tflag == FTW_F || tflag == FTW_D) {
+ getused_size += statp->st_blocks * 512;
+ }
+
+ return 0;
+}
+
+#define GETUSED_CACHETIME 5
+/*!
+ * Calculate used size of a volume with nftw
+ *
+ * The result is cached, we're try to avoid frequently calling nftw()
+ *
+ * 1) Call nftw an refresh if:
+ * 1a) - we're called the first time
+ * 1b) - volume modification date is not yet set and the last time we've been called is
+ * longer then 30 sec ago
+ * 1c) - the last volume modification is less then 30 sec old
+ *
+ * @param vol (rw) volume to calculate
+ */
+static int getused(struct vol *vol)
+{
+ static time_t vol_mtime = 0;
+ int ret = 0;
+ time_t now = time(NULL);
+
+ if (!vol_mtime
+ || (!vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < now))
+ || (vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < vol->v_mtime))
+ ) {
+ vol_mtime = now;
+ getused_size = 0;
+ vol->v_written = 0;
+ ret = nftw(vol->v_path, getused_stat, NULL, 20, FTW_PHYS); /* 2 */
+ LOG(log_debug, logtype_afpd, "volparams: from nftw: %" PRIu64 " bytes",
+ getused_size);
+ } else {
+ getused_size += vol->v_written;
+ vol->v_written = 0;
+ LOG(log_debug, logtype_afpd, "volparams: cached used: %" PRIu64 " bytes",
+ getused_size);
+ }
+
+ return ret;
+}
+
static int getvolspace(struct vol *vol,
u_int32_t *bfree, u_int32_t *btotal,
VolSpace *xbfree, VolSpace *xbtotal, u_int32_t *bsize)
{
int spaceflag, rc;
u_int32_t maxsize;
+ VolSpace used;
#ifndef NO_QUOTA_SUPPORT
VolSpace qfree, qtotal;
#endif
getvolspace_done:
if (vol->v_limitsize) {
- bstring cmdstr;
- if ((cmdstr = bformat("du -sh \"%s\" 2> /dev/null | cut -f1", vol->v_path)) == NULL)
+ if (getused(vol) != 0)
return AFPERR_MISC;
-
- FILE *cmd = popen(cfrombstr(cmdstr), "r");
- bdestroy(cmdstr);
- if (cmd == NULL)
- return AFPERR_MISC;
-
- char buf[100];
- fgets(buf, 100, cmd);
-
- if (pclose(cmd) == -1)
- return AFPERR_MISC;
-
- size_t multi = 0;
- if (buf[strlen(buf) - 2] == 'G' || buf[strlen(buf) - 2] == 'g')
- /* GB */
- multi = 1024 * 1024 * 1024;
- else if (buf[strlen(buf) - 2] == 'M' || buf[strlen(buf) - 2] == 'm')
- /* MB */
- multi = 1024 * 1024;
- else if (buf[strlen(buf) - 2] == 'K' || buf[strlen(buf) - 2] == 'k')
- /* MB */
- multi = 1024;
-
- char *p;
- if (p = strchr(buf, ','))
- /* ignore fraction */
- *p = 0;
- else
- /* remove G|M|K char */
- buf[strlen(buf) - 2] = 0;
- /* now buf contains only digits */
- long long used = atoll(buf) * multi;
- LOG(log_debug, logtype_afpd, "volparams: used on volume: %llu bytes", used);
+ LOG(log_debug, logtype_afpd, "volparams: used on volume: %" PRIu64 " bytes",
+ getused_size);
+ vol->v_tm_used = getused_size;
*xbtotal = min(*xbtotal, (vol->v_limitsize * 1024 * 1024));
- *xbfree = min(*xbfree, *xbtotal < used ? 0 : *xbtotal - used);
+ *xbfree = min(*xbfree, *xbtotal < getused_size ? 0 : *xbtotal - getused_size);
}
*bfree = min( *xbfree, maxsize);
return( AFP_OK );
}
+#define FCE_TM_DELTA 10 /* send notification every 10 seconds */
+void vol_fce_tm_event(void)
+{
+ static time_t last;
+ time_t now = time(NULL);
+ struct vol *vol = Volumes;
+
+ if ((last + FCE_TM_DELTA) < now) {
+ last = now;
+ for ( ; vol; vol = vol->v_next ) {
+ if (vol->v_flags & AFPVOL_TM)
+ (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_written);
+ }
+ }
+}
+
/* -----------------------
* set volume creation date
* avoid duplicate, well at least it tries
ashort |= VOLPBIT_ATTR_UNIXPRIV;
if (vol->v_flags & AFPVOL_TM)
ashort |= VOLPBIT_ATTR_TM;
-
+ if (vol->v_flags & AFPVOL_NONETIDS)
+ ashort |= VOLPBIT_ATTR_NONETIDS;
if (afp_version >= 32) {
if (vol->v_vfs_ea)
ashort |= VOLPBIT_ATTR_EXT_ATTRS;
{
struct timeval tv;
- /* just looking at vol->v_mtime is broken seriously since updates
- * from other users afpd processes never are seen.
- * This is not the most elegant solution (a shared memory between
- * the afpd processes would come closer)
- * [RS] */
-
if ( gettimeofday( &tv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
return;
#include <atalk/volume.h>
#include <atalk/cnid.h>
#include <atalk/unicode.h>
-
-#include "globals.h"
-#if 0
-#include "hash.h"
-#endif
+#include <atalk/globals.h>
extern struct vol *getvolbyvid (const u_int16_t);
extern int ustatfs_getvolspace (const struct vol *,
extern void load_volumes (AFPObj *obj);
extern const struct vol *getvolumes(void);
extern void unload_volumes_and_extmap(void);
+extern void vol_fce_tm_event(void);
/* FP functions */
int afp_openvol (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
sigprocmask(SIG_BLOCK, &set, NULL);
}
+static int setlimits(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) {
+ rlim.rlim_cur = 65535;
+ if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535)
+ rlim.rlim_max = 65535;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
/* ------------------ */
int main(int argc, char *argv[])
{
daemon_exit(1);
}
+ (void)setlimits();
+
/* Check PID lockfile and become a daemon */
switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, debug)) {
case -1: /* error */
#include <atalk/afp.h>
#include <atalk/uam.h>
-#include "../afpd/globals.h"
+#include <atalk/globals.h>
/* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
#define PRIMEBITS 1024
compat.h \
dsi.h \
ldapconfig.h \
- list.h
+ list.h \
+ globals.h \
+ fce_api.h
BUILT_SOURCES = lockrpc.gen.h
EXTRADIST = lockrpc.gen.h
lockrpc.gen.h: $(top_srcdir)/libevent/event_rpcgen.py $(top_srcdir)/libatalk/rpc/lockrpc.rpc
cd $(top_srcdir)/libatalk/rpc/ \
&& $(top_srcdir)/libevent/event_rpcgen.py lockrpc.rpc \
- && mv lockrpc.gen.h $(top_srcdir)/include/atalk
\ No newline at end of file
+ && mv lockrpc.gen.h $(top_srcdir)/include/atalk
uint32_t d_offcnt; /* offspring count */
uint16_t d_vid; /* only needed in the dircache, because
we put all directories in one cache. */
+ uint32_t d_rights_cache; /* cached rights combinded from mode and possible ACL */
+
/* Stuff used in the dircache */
time_t dcache_ctime; /* inode ctime, used and modified by dircache */
ino_t dcache_ino; /* inode number, used to detect changes in the dircache */
#include <sys/socket.h>
#include <signal.h>
#include <arpa/inet.h>
-
#include <netinet/in.h>
+
#include <atalk/afp.h>
#include <atalk/server_child.h>
+#include <atalk/globals.h>
/* What a DSI packet looks like:
0 32
/* child and parent processes might interpret a couple of these
* differently. */
typedef struct DSI {
+ AFPObj *AFPobj;
dsi_proto protocol;
struct dsi_block header;
struct sockaddr_storage server, client;
#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 */
+#define DSI_GOT_ECONNRESET (1 << 10) /* got ECONNRESET from client => exit */
/* basic initialization: dsi_init.c */
extern DSI *dsi_init (const dsi_proto /*protocol*/,
extern size_t dsi_stream_read (DSI *, void *, const size_t);
extern int dsi_stream_send (DSI *, void *, size_t);
extern int dsi_stream_receive (DSI *, void *, const size_t, size_t *);
+extern int dsi_disconnect(DSI *dsi);
#ifdef WITH_SENDFILE
extern ssize_t dsi_stream_read_file(DSI *, int, off_t off, const size_t len);
--- /dev/null
+/*
+ * File: fce_api.h
+ * Author: mw
+ *
+ * Created on 1. Oktober 2010, 21:35
+ *
+ * API calls for file change event api
+ */
+
+#ifndef _FCE_API_H
+#define _FCE_API_H
+
+/* fce_packet.mode */
+#define FCE_FILE_MODIFY 1
+#define FCE_FILE_DELETE 2
+#define FCE_DIR_DELETE 3
+#define FCE_FILE_CREATE 4
+#define FCE_DIR_CREATE 5
+#define FCE_TM_SIZE 6
+#define FCE_CONN_START 42
+#define FCE_CONN_BROKEN 99
+
+
+/* fce_packet.fce_magic */
+#define FCE_PACKET_MAGIC "at_fcapi"
+
+/* This packet goes over the network, so we want to
+ * be shure about datastructs and type sizes between platforms.
+ * Format is network byte order.
+ */
+#define FCE_PACKET_HEADER_SIZE 8+1+1+4+2
+struct fce_packet
+{
+ char magic[8];
+ unsigned char version;
+ unsigned char mode;
+ uint32_t event_id;
+ uint16_t datalen;
+ char data[MAXPATHLEN];
+};
+
+struct path;
+struct ofork;
+
+int fce_register_delete_file( struct path *path );
+int fce_register_delete_dir( char *name );
+int fce_register_new_dir( struct path *path );
+int fce_register_new_file( struct path *path );
+int fce_register_file_modification( struct ofork *ofork );
+int fce_register_tm_size(const char *vol, size_t used);
+
+int fce_add_udp_socket(const char *target ); // IP or IP:Port
+int fce_set_coalesce( char *coalesce_opt ); // all|delete|create
+int fce_set_events(const char *events); /* fmod,fdel,ddel,fcre,dcre,tmsz (default is all except tmsz) */
+
+#define FCE_DEFAULT_PORT 12250
+#define FCE_DEFAULT_PORT_STRING "12250"
+
+#endif /* _FCE_API_H */
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_GLOBALS_H
+#define AFPD_GLOBALS_H 1
+
+#include <sys/param.h>
+
+#ifdef ADMIN_GRP
+#include <grp.h>
+#include <sys/types.h>
+#endif /* ADMIN_GRP */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h> /* this isn't header-protected under ultrix */
+#endif /* HAVE_NETDB_H */
+
+#include <netatalk/at.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/unicode.h>
+#include <atalk/uam.h>
+
+/* #define DOSFILELEN 12 */ /* Type1, DOS-compat*/
+#define MACFILELEN 31 /* Type2, HFS-compat */
+#define UTF8FILELEN_EARLY 255 /* Type3, early Mac OS X 10.0-10.4.? */
+/* #define UTF8FILELEN_NAME_MAX 765 */ /* Type3, 10.4.?- , getconf NAME_MAX */
+/* #define UTF8FILELEN_SPEC 0xFFFF */ /* Type3, spec on document */
+/* #define HFSPLUSFILELEN 510 */ /* HFS+ spec, 510byte = 255codepoint */
+
+#define MAXUSERLEN 256
+
+#define OPTION_DEBUG (1 << 0)
+#define OPTION_USERVOLFIRST (1 << 1)
+#define OPTION_NOUSERVOL (1 << 2)
+#define OPTION_PROXY (1 << 3)
+#define OPTION_CUSTOMICON (1 << 4)
+#define OPTION_NOSLP (1 << 5)
+#define OPTION_ANNOUNCESSH (1 << 6)
+#define OPTION_UUID (1 << 7)
+#define OPTION_ACL2MACCESS (1 << 8)
+#define OPTION_NOZEROCONF (1 << 9)
+
+/* a couple of these options could get stuck in unions to save
+ * space. */
+struct afp_volume_name {
+ time_t mtime;
+ char *name;
+ char *full_name;
+ int loaded;
+};
+
+struct afp_options {
+ int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
+ int sleep; /* Maximum time allowed to sleep (in tickles) */
+ int disconnected; /* Maximum time in disconnected state (in tickles) */
+ unsigned int tcp_sndbuf, tcp_rcvbuf;
+ unsigned char passwdbits, passwdminlen, loginmaxfail;
+ u_int32_t server_quantum;
+ int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
+ char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
+ char *uampath, *fqdn;
+ char *pidfile;
+ char *sigconffile;
+ char *uuidconf;
+ struct afp_volume_name defaultvol, systemvol, uservol;
+ int closevol;
+
+ char *guest, *loginmesg, *keyfile, *passwdfile;
+ char *uamlist;
+ char *authprintdir;
+ char *signatureopt;
+ unsigned char signature[16];
+ char *k5service, *k5realm, *k5keytab;
+ char *unixcodepage,*maccodepage;
+ charset_t maccharset, unixcharset;
+ mode_t umask;
+ mode_t save_mask;
+#ifdef ADMIN_GRP
+ gid_t admingid;
+#endif /* ADMIN_GRP */
+ int volnamelen;
+
+ /* default value for winbind authentication */
+ char *ntdomain, *ntseparator;
+ char *logconfig;
+
+ char *mimicmodel;
+};
+
+#define AFPOBJ_TMPSIZ (MAXPATHLEN)
+typedef struct _AFPObj {
+ int proto;
+ unsigned long servernum;
+ void *handle; /* either (DSI *) or (ASP *) */
+ void *config;
+ struct afp_options options;
+ char *Obj, *Type, *Zone;
+ char username[MAXUSERLEN];
+ void (*logout)(void), (*exit)(int);
+ int (*reply)(void *, int);
+ int (*attention)(void *, AFPUserBytes);
+ /* to prevent confusion, only use these in afp_* calls */
+ char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
+ void *uam_cookie; /* cookie for uams */
+ struct session_info sinfo;
+ uid_t uid; /* client running user id */
+ int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
+} AFPObj;
+
+/* typedef for AFP functions handlers */
+typedef int (*AFPCmd)(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
+
+/* afp_dsi.c */
+extern AFPObj *AFPobj;
+
+extern int afp_version;
+extern int afp_errno;
+extern unsigned char nologin;
+extern struct dir *curdir;
+extern char getwdbuf[];
+
+/* FIXME CNID */
+extern const char *Cnid_srv;
+extern const char *Cnid_port;
+
+extern int get_afp_errno (const int param);
+extern void afp_options_init (struct afp_options *);
+extern int afp_options_parse (int, char **, struct afp_options *);
+extern int afp_options_parseline (char *, struct afp_options *);
+extern void afp_options_free (struct afp_options *,
+ const struct afp_options *);
+extern void setmessage (const char *);
+extern void readmessage (AFPObj *);
+
+/* gettok.c */
+extern void initline (int, char *);
+extern int parseline (int, char *);
+
+/* afp_util.c */
+extern const char *AfpNum2name (int );
+extern const char *AfpErr2name(int err);
+
+/* directory.c */
+extern struct dir rootParent;
+
+extern void afp_over_dsi (AFPObj *);
+
+#endif /* globals.h */
# define _PATH_AFPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"afpd.pid")
#else
# define _PATH_AFPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"afpd")
+#define _PATH_AFP_IPC ATALKPATHCAT(_PATH_LOCKDIR,"afpd_ipc")
#endif
/*
pid_t pid; /* afpd worker process pid (from the worker afpd process )*/
uid_t uid; /* user id of connected client (from the worker afpd process) */
int valid; /* 1 if we have a clientid */
- uint32_t time; /* client boot time (from the mac client) */
int killed; /* 1 if we already tried to kill the client */
+ int disasociated; /* 1 if this is not a child, but a child from a previous afpd master */
+ uint32_t time; /* client boot time (from the mac client) */
uint32_t idlen; /* clientid len (from the Mac client) */
char *clientid; /* clientid (from the Mac client) */
int ipc_fds[2]; /* socketpair for IPC bw */
#define ATALK_SERVER_IPC_H
#include <atalk/server_child.h>
+#include <atalk/globals.h>
#define IPC_DISCOLDSESSION 0
#define IPC_GETSESSION 1
-int ipc_server_read(server_child *children, int fd);
-int ipc_child_write(int fd, uint16_t command, int len, void *token);
+extern int ipc_server_uds(const char *name);
+extern int ipc_client_uds(const char *name);
+extern int reconnect_ipc(AFPObj *);
+extern int ipc_server_read(server_child *children, int fd);
+extern int ipc_child_write(int fd, uint16_t command, int len, void *token);
#endif /* IPC_GETSESSION_LOGIN */
size_t cryptedkey_len;
void *sessiontoken; /* session token sent to the client on FPGetSessionToken*/
size_t sessiontoken_len;
+ void *clientid; /* whole buffer cotaining eg idlen, id and boottime */
+ size_t clientid_len;
};
/* register and unregister uams with these functions */
#define ZERO_STRUCT(a) memset(&(a), 0, sizeof(a))
#define ZERO_STRUCTP(a) memset((a), 0, sizeof(a))
+#if BYTE_ORDER == BIG_ENDIAN
+#define hton64(x) (x)
+#define ntoh64(x) (x)
+#else /* BYTE_ORDER == BIG_ENDIAN */
+#define hton64(x) ((uint64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
+ (uint64_t) ((htonl(x) & 0xffffffffLL) << 32))
+#define ntoh64(x) (hton64(x))
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
#ifdef WITH_SENDFILE
extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
#endif
extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
/* Structures and functions dealing with dynamic pollfd arrays */
-enum fdtype {IPC_FD, LISTEN_FD};
+enum fdtype {IPC_FD, LISTEN_FD, DISASOCIATED_IPC_FD};
struct polldata {
enum fdtype fdtype; /* IPC fd or listening socket fd */
void *data; /* pointer to AFPconfig for listening socket and *
typedef const unsigned char *uuidp_t;
typedef unsigned char atalk_uuid_t[UUID_BINSIZE];
-typedef enum {UUID_USER = 1, UUID_GROUP, UUID_LOCAL} uuidtype_t;
+typedef enum {UUID_USER = 0,
+ UUID_GROUP = 1,
+ UUID_ENOENT = 4} /* used as bit flag */
+ uuidtype_t;
+#define UUIDTYPESTR_MASK 3
extern char *uuidtype[];
/* afp_options.c needs these. defined in libatalk/ldap.c */
extern void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id);
extern const char *uuid_bin2string(const unsigned char *uuid);
extern void uuid_string2bin( const char *uuidstring, unsigned char *uuid);
+extern void uuidcache_dump(void);
#endif /* AFP_UUID_H */
char *v_gvs;
void *v_nfsclient;
int v_nfs;
-
+ uintmax_t v_tm_used; /* used bytes on a TM volume */
+ uintmax_t v_written; /* amount of data written in afp_write, reset every time a FCE_TM_SIZE event is sent */
+
/* only when opening/closing volumes or in error */
int v_casefold;
char *v_localname; /* as defined in AppleVolumes.default */
#define AFPVOL_TM (1 << 23) /* Supports TimeMachine */
#define AFPVOL_ACLS (1 << 24) /* Volume supports ACLS */
#define AFPVOL_SEARCHDB (1 << 25) /* Use fast CNID db search instead of filesystem */
-/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
-#if 0
-#define AFPVOL_CDROM (1 << XX) /* Ejectable media eg CD -> in memory CNID db */
-#endif
+#define AFPVOL_NONETIDS (1 << 26) /* signal the client it shall do privelege mapping */
/* Extended Attributes vfs indirection */
#define AFPVOL_EA_NONE 0 /* No EAs */
#define VOLPBIT_ATTR_BLANKACCESS (1 << 4)
#define VOLPBIT_ATTR_UNIXPRIV (1 << 5)
#define VOLPBIT_ATTR_UTF8 (1 << 6)
-#define VOLPBIT_ATTR_NONETUID (1 << 7)
+#define VOLPBIT_ATTR_NONETIDS (1 << 7)
#define VOLPBIT_ATTR_EXT_ATTRS (1 << 10)
#define VOLPBIT_ATTR_ACLS (1 << 11)
#define VOLPBIT_ATTR_TM (1 << 13)
* helper function
********************************************************/
-static int dumpcache() {
+void uuidcache_dump(void) {
int i;
int ret = 0;
cacheduser_t *entry;
continue;
if (strftime(timestr, 200, "%c", tmp) == 0)
continue;
- LOG(log_debug9, logtype_default, "namecache{%d}: name:%s, uuid:%s, type: %s, cached: %s",
- i, entry->name, uuid_bin2string(entry->uuid), uuidtype[entry->type], timestr);
+ LOG(log_debug, logtype_default,
+ "namecache{%d}: name:%s, uuid:%s, type%s: %s, cached: %s",
+ i,
+ entry->name,
+ uuid_bin2string(entry->uuid),
+ (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[entry->type & UUIDTYPESTR_MASK],
+ timestr);
} while ((entry = entry->next) != NULL);
}
}
continue;
if (strftime(timestr, 200, "%c", tmp) == 0)
continue;
- LOG(log_debug9, logtype_default, "uuidcache{%d}: uuid:%s, name:%s, type: %s, cached: %s",
- i, uuid_bin2string(entry->uuid), entry->name, uuidtype[entry->type], timestr);
+ LOG(log_debug, logtype_default,
+ "uuidcache{%d}: uuid:%s, name:%s, type%s: %s, cached: %s",
+ i,
+ uuid_bin2string(entry->uuid),
+ entry->name,
+ (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[entry->type & UUIDTYPESTR_MASK],
+ timestr);
} while ((entry = entry->next) != NULL);
}
}
-
- return ret;
}
/* hash string it into unsigned char */
cacheduser_t *cacheduser = NULL;
unsigned char hash;
-#ifdef DEBUG
- dumpcache();
-#endif
-
/* allocate mem and copy values */
name = malloc(strlen(inname)+1);
if (!name) {
free(cacheduser);
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return ret;
}
-/*
- * Caller provides buffer uuid for result
+/*!
+ * Search cache by name and uuid type
+ *
+ * @args name (r) name to search
+ * @args type (rw) type (user or group) of name, returns found type here which might
+ * mark it as a negative entry
+ * @args uuid (w) found uuid is returned here
+ * @returns 0 on sucess, entry found
+ * -1 no entry found
*/
-int search_cachebyname( const char *name, uuidtype_t type, unsigned char *uuid) {
+int search_cachebyname( const char *name, uuidtype_t *type, unsigned char *uuid) {
int ret;
unsigned char hash;
cacheduser_t *entry;
time_t tim;
-#ifdef DEBUG
- dumpcache();
-#endif
-
hash = hashstring((unsigned char *)name);
if (namecache[hash] == NULL)
entry = namecache[hash];
while (entry) {
ret = strcmp(entry->name, name);
- if (ret == 0 && type == entry->type) {
+ if (ret == 0 && *type == (entry->type & UUIDTYPESTR_MASK)) {
/* found, now check if expired */
tim = time(NULL);
if ((tim - entry->creationtime) > CACHESECONDS) {
- LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\'%s\' in queue {%d}", entry->name, hash);
+ LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\"%s\"", entry->name);
/* remove item */
if (entry->prev) {
/* 2nd to last in queue */
free(entry->name);
free(entry->uuid);
free(entry);
-#ifdef DEBUG
- dumpcache();
-#endif
return -1;
} else {
memcpy(uuid, entry->uuid, UUID_BINSIZE);
-#ifdef DEBUG
- dumpcache();
-#endif
+ *type = entry->type;
return 0;
}
}
entry = entry->next;
}
-#ifdef DEBUG
- dumpcache();
-#endif
+
return -1;
}
cacheduser_t *entry;
time_t tim;
-#ifdef DEBUG
- dumpcache();
-#endif
-
hash = hashuuid(uuidp);
if (! uuidcache[hash])
free(entry->name);
free(entry->uuid);
free(entry);
-#ifdef DEBUG
- dumpcache();
-#endif
return -1;
} else {
*name = malloc(strlen(entry->name)+1);
strcpy(*name, entry->name);
*type = entry->type;
-#ifdef DEBUG
- dumpcache();
-#endif
return 0;
}
}
entry = entry->next;
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return -1;
}
cacheduser_t *entry;
unsigned char hash;
-#ifdef DEBUG
- dumpcache();
-#endif
-
/* allocate mem and copy values */
name = malloc(strlen(inname)+1);
if (!name) {
free(cacheduser);
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return ret;
}
* Interface
********************************************************/
-/*
- * name: search for this name
- * type: of type USER or GROUP
- * uuid: if found copies uuid into this buffer
- * returns 0 on success, !=0 if not found or on errors
- */
-extern int search_cachebyname( const char *name, uuidtype_t type, unsigned char *uuid);
-
-/*
- * inname: name
- * inuuid: uuid
- * type: USER or GROUP
- * (uid: unused)
- * returns 0 on success, !=0 on memory errors
- */
+extern int search_cachebyname( const char *name, uuidtype_t *type, unsigned char *uuid);
extern int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid);
-
-/* same as above but for the uuid cache */
extern int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
extern int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid);
#include "aclldap.h"
#include "cache.h"
-char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"};
+char *uuidtype[] = {"USER", "GROUP", "LOCAL"};
/********************************************************
* Public helper function
static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
0xab, 0xcd, 0xef,
- 0xab, 0xcd, 0xef,
+ 0xab, 0xcd, 0xef,
0xab, 0xcd, 0xef};
static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
return;
}
-/*
+/*
* convert ascii string that can include dashes to binary uuid.
* caller must provide a buffer.
*/
}
-/*!
+/*!
* Convert 16 byte binary uuid to neat ascii represantation including dashes.
- *
+ *
* Returns pointer to static buffer.
*/
const char *uuid_bin2string(const unsigned char *uuid) {
* type: and type (UUID_USER or UUID_GROUP)
* uuid: pointer to uuid_t storage that the caller must provide
* returns 0 on success !=0 on errror
- */
+ */
int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid) {
int ret = 0;
+ uuidtype_t mytype = type;
+ char nulluuid[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
#ifdef HAVE_LDAP
char *uuid_string = NULL;
#endif
- ret = search_cachebyname( name, type, uuid);
+
+ ret = search_cachebyname(name, &mytype, uuid);
+
if (ret == 0) {
/* found in cache */
- LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
+ LOG(log_debug, logtype_afpd,
+ "getuuidfromname{cache}: name: %s, type%s: %s -> UUID: %s",
+ name,
+ (mytype & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[type & UUIDTYPESTR_MASK],
+ uuid_bin2string(uuid));
+ if ((mytype & UUID_ENOENT) == UUID_ENOENT)
+ return -1;
} else {
/* if not found in cache */
#ifdef HAVE_LDAP
if ((ret = ldap_getuuidfromname( name, type, &uuid_string)) == 0) {
uuid_string2bin( uuid_string, uuid);
- LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
+ LOG(log_debug, logtype_afpd, "getuuidfromname{LDAP}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
} else {
LOG(log_debug, logtype_afpd, "getuuidfromname(\"%s\",t:%u): no result from ldap search",
name, type);
struct passwd *pwd;
if ((pwd = getpwnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
- name, uuidtype[type]);
- goto cleanup;
+ name, uuidtype[type & UUIDTYPESTR_MASK]);
+ mytype |= UUID_ENOENT;
+ memcpy(uuid, nulluuid, 16);
+ } else {
+ localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
+ ret = 0;
+ LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
}
- localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
} else {
struct group *grp;
if ((grp = getgrnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
- name, uuidtype[type]);
- goto cleanup;
+ name, uuidtype[type & UUIDTYPESTR_MASK]);
+ mytype |= UUID_ENOENT;
+ memcpy(uuid, nulluuid, 16);
+ } else {
+ localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
+ ret = 0;
+ LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
}
- localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
}
- LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
}
- ret = 0;
- add_cachebyname( name, uuid, type, 0);
+ add_cachebyname(name, uuid, mytype, 0);
}
cleanup:
*
* Caller must free name appropiately.
*/
-int getnamefromuuid(uuidp_t uuidp, char **name, uuidtype_t *type) {
- int ret;
+int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
+ int ret = 0;
+ uid_t uid;
+ gid_t gid;
+ struct passwd *pwd;
+ struct group *grp;
- ret = search_cachebyuuid( uuidp, name, type);
- if (ret == 0) {
+ if (search_cachebyuuid(uuidp, name, type) == 0) {
/* found in cache */
- LOG(log_debug9, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s",
- uuid_bin2string(uuidp), *name, uuidtype[*type]);
- } else {
- /* not found in cache */
-
- /* Check if UUID is a client local one */
- if (memcmp(uuidp, local_user_uuid, 12) == 0
- || memcmp(uuidp, local_group_uuid, 12) == 0) {
- LOG(log_debug, logtype_afpd, "getnamefromuuid: local UUID: %" PRIu32 "",
- ntohl(*(uint32_t *)(uuidp + 12)));
- *type = UUID_LOCAL;
- *name = strdup("UUID_LOCAL");
- return 0;
+ LOG(log_debug, logtype_afpd,
+ "getnamefromuuid{cache}: UUID: %s -> name: %s, type%s: %s",
+ uuid_bin2string(uuidp),
+ *name,
+ (*type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[(*type) & UUIDTYPESTR_MASK]);
+ if ((*type & UUID_ENOENT) == UUID_ENOENT)
+ return -1;
+ return 0;
+ }
+
+ /* not found in cache */
+
+ /* Check if UUID is a client local one */
+ if (memcmp(uuidp, local_user_uuid, 12) == 0) {
+ *type = UUID_USER;
+ uid = ntohl(*(uint32_t *)(uuidp + 12));
+ if ((pwd = getpwuid(uid)) == NULL) {
+ /* not found, add negative entry to cache */
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ ret = -1;
+ } else {
+ *name = strdup(pwd->pw_name);
+ add_cachebyuuid(uuidp, *name, *type, 0);
+ ret = 0;
}
+ LOG(log_debug, logtype_afpd,
+ "getnamefromuuid{local}: UUID: %s -> name: %s, type:%s",
+ uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+ return ret;
+ } else if (memcmp(uuidp, local_group_uuid, 12) == 0) {
+ *type = UUID_GROUP;
+ gid = ntohl(*(uint32_t *)(uuidp + 12));
+ if ((grp = getgrgid(gid)) == NULL) {
+ /* not found, add negative entry to cache */
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ ret = -1;
+ } else {
+ *name = strdup(grp->gr_name);
+ add_cachebyuuid(uuidp, *name, *type, 0);
+ ret = 0;
+ }
+ return ret;
+ }
#ifdef HAVE_LDAP
- ret = ldap_getnamefromuuid(uuid_bin2string(uuidp), name, type);
- if (ret != 0) {
- LOG(log_warning, logtype_afpd, "getnamefromuuid(%s): no result from ldap_getnamefromuuid",
- uuid_bin2string(uuidp));
- goto cleanup;
- }
- add_cachebyuuid( uuidp, *name, *type, 0);
- LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",
- uuid_bin2string(uuidp), *name, uuidtype[*type]);
-#endif
+ ret = ldap_getnamefromuuid(uuid_bin2string(uuidp), name, type);
+ if (ret != 0) {
+ LOG(log_warning, logtype_afpd, "getnamefromuuid(%s): no result from ldap_getnamefromuuid",
+ uuid_bin2string(uuidp));
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ return -1;
}
+#endif
-cleanup:
- return ret;
+ add_cachebyuuid(uuidp, *name, *type, 0);
+
+ LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",
+ uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+
+ return 0;
}
ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
if (ret != sizeof(struct cnid_dbd_rply)) {
- LOG(log_error, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
+ LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
db->db_dir, ret == -1 ? strerror(errno) : "closed");
rply->name = nametmp;
return -1;
default: /* parent */
/* using SIGQUIT is hokey, but the child might not have
* re-established its signal handler for SIGTERM yet. */
- if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
+ if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) == NULL) {
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = DSIERR_SERVBUSY;
return 0;
}
+/*
+ * Return all bytes up to count from dsi->buffer if there are any buffered there
+ */
+static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
+{
+ size_t nbe = 0;
+
+ if (dsi->buffer == NULL)
+ /* afpd master has no DSI buffering */
+ return 0;
+
+ LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count);
+
+ nbe = dsi->eof - dsi->start;
+
+ if (nbe > 0) {
+ nbe = min((size_t)nbe, count);
+ memcpy(buf, dsi->start, nbe);
+ dsi->start += nbe;
+
+ if (dsi->eof == dsi->start)
+ dsi->start = dsi->eof = dsi->buffer;
+ }
+
+ LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u",
+ dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe);
+
+ return nbe;
+}
+
+/*
+ * Get bytes from buffer dsi->buffer or read from socket
+ *
+ * 1. Check if there are bytes in the the dsi->buffer buffer.
+ * 2. Return bytes from (1) if yes.
+ * Note: this may return fewer bytes then requested in count !!
+ * 3. If the buffer was empty, read from the socket.
+ */
+static ssize_t buf_read(DSI *dsi, u_int8_t *buf, size_t count)
+{
+ ssize_t len;
+
+ LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count);
+
+ if (!count)
+ return 0;
+
+ len = from_buf(dsi, buf, count); /* 1. */
+ if (len)
+ return len; /* 2. */
+
+ len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
+
+ LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
+
+ return len;
+}
+
+/*
+ * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads
+ * this tries to read larger chunks (8192 bytes) into a buffer.
+ */
+static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t length)
+{
+ size_t len;
+ size_t buflen;
+
+ LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length);
+
+ len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */
+ dsi->read_count += len;
+ if (len == length) { /* got enough bytes from there ? */
+ return len; /* yes */
+ }
+
+ /* fill the buffer with 8192 bytes or until buffer is full */
+ buflen = min(8192, dsi->end - dsi->eof);
+ if (buflen > 0) {
+ ssize_t ret;
+ ret = read(dsi->socket, dsi->eof, buflen);
+ if (ret > 0)
+ dsi->eof += ret;
+ }
+
+ /* now get the remaining data */
+ if ((buflen = dsi_stream_read(dsi, data + len, length - len)) != length - len)
+ return 0;
+ len += buflen;
+
+ return len;
+}
+
+/* ---------------------------------------
+*/
+static void block_sig(DSI *dsi)
+{
+ dsi->in_write++;
+}
+
+/* ---------------------------------------
+*/
+static void unblock_sig(DSI *dsi)
+{
+ dsi->in_write--;
+}
+
+/*********************************************************************************
+ * Public functions
+ *********************************************************************************/
+
+/*!
+ * Communication error with the client, enter disconnected state
+ *
+ * 1. close the socket
+ * 2. set the DSI_DISCONNECTED flag
+ *
+ * @returns 0 if successfully entered disconnected state
+ * -1 if ppid is 1 which means afpd master died
+ * or euid == 0 ie where still running as root (unauthenticated session)
+ */
+int dsi_disconnect(DSI *dsi)
+{
+ dsi->proto_close(dsi); /* 1 */
+ dsi->flags |= DSI_DISCONNECTED; /* 2 */
+ if (geteuid() == 0)
+ return -1;
+ return 0;
+}
+
/* ------------------------------
* write raw data. return actual bytes read. checks against EINTR
* aren't necessary if all of the signals have SA_RESTART
LOG(log_maxdebug, logtype_dsi, "dsi_stream_write: sending %u bytes", length);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return -1;
+
while (written < length) {
len = send(dsi->socket, (u_int8_t *) data + written, length - written, flags);
if (len >= 0) {
LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: sending %u bytes", length);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return -1;
+
dsi->in_write++;
written = 0;
}
#endif
-/*
- * Return all bytes up to count from dsi->buffer if there are any buffered there
- */
-static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
-{
- size_t nbe = 0;
-
- if (dsi->buffer == NULL)
- /* afpd master has no DSI buffering */
- return 0;
-
- LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count);
-
- nbe = dsi->eof - dsi->start;
-
- if (nbe > 0) {
- nbe = min((size_t)nbe, count);
- memcpy(buf, dsi->start, nbe);
- dsi->start += nbe;
-
- if (dsi->eof == dsi->start)
- dsi->start = dsi->eof = dsi->buffer;
- }
-
- LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u",
- dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe);
-
- return nbe;
-}
-
-/*
- * Get bytes from buffer dsi->buffer or read from socket
- *
- * 1. Check if there are bytes in the the dsi->buffer buffer.
- * 2. Return bytes from (1) if yes.
- * Note: this may return fewer bytes then requested in count !!
- * 3. If the buffer was empty, read from the socket.
- */
-static ssize_t buf_read(DSI *dsi, u_int8_t *buf, size_t count)
-{
- ssize_t len;
-
- LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count);
-
- if (!count)
- return 0;
-
- len = from_buf(dsi, buf, count); /* 1. */
- if (len)
- return len; /* 2. */
-
- len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
-
- LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
-
- return len;
-}
/*
* Essentially a loop around buf_read() to ensure "length" bytes are read
size_t stored;
ssize_t len;
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes)", length);
stored = 0;
while (stored < length) {
len = buf_read(dsi, (u_int8_t *) data + stored, length - stored);
if (len == -1 && (errno == EINTR || errno == EAGAIN)) {
- LOG(log_debug, logtype_dsi, "dsi_stream_read: select read loop");
+ LOG(log_maxdebug, logtype_dsi, "dsi_stream_read: select read loop");
continue;
} else if (len > 0) {
stored += len;
} else { /* eof or error */
/* don't log EOF error if it's just after connect (OSX 10.3 probe) */
+ if (errno == ECONNRESET)
+ dsi->flags |= DSI_GOT_ECONNRESET;
if (len || stored || dsi->read_count) {
if (! (dsi->flags & DSI_DISCONNECTED)) {
LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
return stored;
}
-/*
- * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads
- * this tries to read larger chunks (8192 bytes) into a buffer.
- */
-static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t length)
-{
- size_t len;
- size_t buflen;
-
- LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length);
-
- len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */
- dsi->read_count += len;
- if (len == length) { /* got enough bytes from there ? */
- return len; /* yes */
- }
-
- /* fill the buffer with 8192 bytes or until buffer is full */
- buflen = min(8192, dsi->end - dsi->eof);
- if (buflen > 0) {
- ssize_t ret;
- ret = read(dsi->socket, dsi->eof, buflen);
- if (ret > 0)
- dsi->eof += ret;
- }
-
- /* now get the remaining data */
- len += dsi_stream_read(dsi, data + len, length - len);
- return len;
-}
-
-/* ---------------------------------------
-*/
-static void block_sig(DSI *dsi)
-{
- dsi->in_write++;
-}
-
-/* ---------------------------------------
-*/
-static void unblock_sig(DSI *dsi)
-{
- dsi->in_write--;
-}
-
/* ---------------------------------------
* write data. 0 on failure. this assumes that dsi_len will never
* cause an overflow in the data buffer.
LOG(log_maxdebug, logtype_dsi, "dsi_stream_send: %u bytes",
length ? length : sizeof(block));
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
block[0] = dsi->header.dsi_flags;
block[1] = dsi->header.dsi_command;
memcpy(block + 2, &dsi->header.dsi_requestID,
LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: %u bytes", ilength);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
/* read in the header */
if (dsi_buffered_stream_read(dsi, (u_int8_t *)block, sizeof(block)) != sizeof(block))
return 0;
if (type_configs[logtype].level >= log_debug)
goto log; /* bypass flooding checks */
+ goto log;
/* Prevent flooding: hash the message and check if we got the same one recently */
int hash = hash_message(temp_buffer) + log_src_linenumber;
fork = (server_child_fork *) children->fork + forkid;
if ((child = resolve_child(fork->table, 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);
+ kill(pid, SIGTERM);
+ sleep(2);
+ if (kill(pid, 0) == 0) {
+ LOG(log_error, logtype_default, "Reconnect: killing old session[%u]", pid);
+ kill(pid, SIGKILL);
+ sleep(2);
+ }
+ }
return 0;
}
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <errno.h>
#include <signal.h>
+#include <time.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
#include <atalk/logger.h>
#include <atalk/util.h>
+#include <atalk/errchk.h>
+#include <atalk/paths.h>
+#include <atalk/globals.h>
+#include <atalk/dsi.h>
+
+#define IPC_HEADERLEN 14
+#define IPC_MAXMSGSIZE 90
typedef struct ipc_header {
uint16_t command;
return 0;
}
-#define IPC_HEADERLEN 14
-#define IPC_MAXMSGSIZE 90
+/***********************************************************************************
+ * Public functions
+ ***********************************************************************************/
+
+/*!
+ * Listen on UNIX domain socket "name" for IPC from old sesssion
+ *
+ * @args name (r) file name to use for UNIX domain socket
+ * @returns socket fd, -1 on error
+ */
+int ipc_server_uds(const char *name)
+{
+ EC_INIT;
+ struct sockaddr_un address;
+ socklen_t address_length;
+ int fd = -1;
+
+ EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
+ EC_ZERO_LOG( setnonblock(fd, 1) );
+ unlink(name);
+ address.sun_family = AF_UNIX;
+ address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name);
+ EC_ZERO_LOG( bind(fd, (struct sockaddr *)&address, address_length) );
+ EC_ZERO_LOG( listen(fd, 1024) );
+
+EC_CLEANUP:
+ if (ret != 0) {
+ return -1;
+ }
+
+ return fd;
+}
+
+/*!
+ * Connect to UNIX domain socket "name" for IPC with new afpd master
+ *
+ * 1. Connect
+ * 2. send pid, which establishes a child structure for us in the master
+ *
+ * @args name (r) file name to use for UNIX domain socket
+ * @returns socket fd, -1 on error
+ */
+int ipc_client_uds(const char *name)
+{
+ EC_INIT;
+ struct sockaddr_un address;
+ socklen_t address_length;
+ int fd = -1;
+ pid_t pid = getpid();
+
+ EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
+ EC_ZERO_LOG( setnonblock(fd, 1) );
+ address.sun_family = AF_UNIX;
+ address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name);
+
+ EC_ZERO_LOG( connect(fd, (struct sockaddr *)&address, address_length) ); /* 1 */
+ LOG(log_debug, logtype_afpd, "ipc_client_uds: connected to master");
+
+ if (writet(fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
+ LOG(log_error, logtype_afpd, "ipc_client_uds: writet: %s", strerror(errno));
+ EC_FAIL;
+ }
+
+EC_CLEANUP:
+ if (ret != 0) {
+ return -1;
+ }
+ LOG(log_debug, logtype_afpd, "ipc_client_uds: fd: %d", fd);
+ return fd;
+}
+
+int reconnect_ipc(AFPObj *obj)
+{
+ int retrycount = 0;
+
+ LOG(log_debug, logtype_afpd, "reconnect_ipc: start");
+
+ close(obj->ipc_fd);
+ obj->ipc_fd = -1;
+
+ srandom(getpid());
+ sleep((random() % 5) + 5); /* give it enough time to start */
+
+ while (retrycount++ < 10) {
+ if ((obj->ipc_fd = ipc_client_uds(_PATH_AFP_IPC)) == -1) {
+ LOG(log_error, logtype_afpd, "reconnect_ipc: cant reconnect to master");
+ sleep(1);
+ continue;
+ }
+ LOG(log_debug, logtype_afpd, "reconnect_ipc: succesfull IPC reconnect");
+ return 0;
+ }
+ return -1;
+}
/* -----------------
* Ipc format
* pid
* uid
*
-*/
+ */
/*!
* Read a IPC message from a child
char block[IPC_MAXMSGSIZE], *p;
pid_t pid;
uid_t uid;
+ ssize_t ret;
+
p = block;
memset ( p, 0 , IPC_MAXMSGSIZE);
LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
- return write(fd, block, len+IPC_HEADERLEN );
+ if ((ret = writet(fd, block, len+IPC_HEADERLEN, 0, 1)) != len + IPC_HEADERLEN) {
+ return -1;
+ }
+
+ return 0;
}
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 600
-#endif
-#ifndef __EXTENSIONS__
-# define __EXTENSIONS__
-#endif
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
+#if !defined(__FreeBSD__)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+# endif
+# ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+# endif
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
#endif
#include <unistd.h>
#include <fcntl.h>
goto exit;
default: /* -1 */
- if (errno == EINTR) {
+ switch (errno) {
+ case EINTR:
(void)gettimeofday(&now, NULL);
if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
- LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+ LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
goto exit;
}
if (now.tv_usec > end.tv_usec) {
}
FD_SET(socket, &rfds);
continue;
+ case EBADF:
+ /* possibly entered disconnected state, don't spam log here */
+ LOG(log_debug, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ default:
+ LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
}
- LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
- stored = -1;
- goto exit;
}
} /* while (select) */
continue;
acl_perm_t perm;
return acl_get_perm_np(permset_d, perm);
],
- [samba_cv_HAVE_ACL_GET_PERM_NP=yes],
- [samba_cv_HAVE_ACL_GET_PERM_NP=no])
+ [netatalk_cv_HAVE_ACL_GET_PERM_NP=yes],
+ [netatalk_cv_HAVE_ACL_GET_PERM_NP=no])
LIBS=$acl_LIBS
])
if test x"netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
fi
+
+
+ AC_CACHE_CHECK([for acl_from_mode], netatalk_cv_HAVE_ACL_FROM_MODE,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS $ACL_LIBS"
+ AC_CHECK_FUNCS(acl_from_mode,
+ [netatalk_cv_HAVE_ACL_FROM_MODE=yes],
+ [netatalk_cv_HAVE_ACL_FROM_MODE=no])
+ LIBS=$acl_LIBS
+ ])
+ if test x"netatalk_cv_HAVE_ACL_FROM_MODE" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_FROM_MODE,1,[Whether acl_from_mode() is available])
+ fi
+
else
AC_MSG_NOTICE(ACL support is not avaliable)
AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
fi
if test x"$with_acl_support" = x"yes" ; then
+ AC_CHECK_HEADERS([acl/libacl.h])
AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
AC_SUBST(ACL_LIBS)
fi
.\" Title: afppasswd
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 22 Aug 2004
+.\" Date: 31 May 2011
.\" Manual: Netatalk 2.2
.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "AFPPASSWD" "1" "22 Aug 2004" "Netatalk 2.2" "Netatalk 2.2"
+.TH "AFPPASSWD" "1" "31 May 2011" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.ps -1
.br
.PP
-With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" UAM instead\&. Please compare with the
+With this utility you can only change the passwords used by two specific UAMs\&. As they provide only weak password encryption, the use of the "Randnum exchange" and "2\-Way Randnum exchange" UAMs is deprecated unless one has to support very old AFP clients, that can not deal with the more secure "DHCAST128" and "DHX2" UAM instead\&. Please compare with the
Authentication chapter
inside Netatalk\'s documentation\&.
.sp .5v
\fBusedots\fR: make dot files invisible\&.
.RE
.PP
+nonetids
+.RS 4
+Try to force ACL unawareness on the client\&.
+.RE
+.PP
limitsize
.RS 4
Limit disk size reporting to 2GB\&. This can be used for older Macintoshes using newer Appleshare clients\&.
.PP
upriv
.RS 4
-use AFP3 unix privileges\&. Become familiar with the new "unix privileges" AFP permissions concepts in MacOS X before using this option\&. See also:
+use AFP3 unix privileges\&. This should be set for OS X clients\&. Starting with Netatalk 2\&.1 it\'s part of the default config :DEFAULT: line\&. See also:
\fBperm|fperm|dperm\fR\&.
.RE
.PP
Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
.RE
.PP
+\-fcelistener \fIhost[:port]\fR
+.RS 4
+Enables sending FCE events to the specified
+\fIhost\fR, default
+\fIport\fR
+is 12250 if not specified\&. Specifying mutliple listeners is done by having this option once for each of them\&.
+.RE
+.PP
+\-fceevents \fIfmod,fdel,ddel,fcre,dcre,tmsz\fR
+.RS 4
+Speficies which FCE events are active, default is
+\fIfmod,fdel,ddel,fcre,dcre\fR\&.
+.RE
+.PP
+\-fcecoalesce \fIall|delete|create\fR
+.RS 4
+Coalesce FCE events\&.
+.RE
+.PP
\-guestname \fI[name]\fR
.RS 4
Specifies the user that guests should use (default is "nobody")\&. The name should be quoted\&.
and should be quoted\&. Extended characters are allowed\&.
.RE
.PP
+\-mimicmodel \fImodel\fR
+.RS 4
+Specifies the icon model that appears on clients\&. Defaults to off\&. Examples: RackMac (same as Xserve), PowerBook, PowerMac, Macmini, iMac, MacBook, MacBookPro, MacBookAir, MacPro, AppleTV1,1, AirPort\&.
+.RE
+.PP
\-nodebug
.RS 4
Disables debugging\&.
.RE
.SH "SIGNALS"
.PP
-Signals that are sent to the main
-\fBafpd\fR
-process are propagated to the children, so all will be affected\&.
-.PP
To shut down a user\'s
\fBafpd\fR
process it is recommended that
\fBSIGTERM (\-15)\fR
signal and wait for it to die on its own\&.
.PP
+SIGTERM and SIGUSR1 signals that are sent to the main
+\fBafpd\fR
+process are propagated to the children, so all will be affected\&.
+.PP
+SIGTERM
+.RS 4
+Clean exit\&. Propagates from master to childs\&.
+.RE
+.PP
+SIGQUIT
+.RS 4
+Send this to the master
+\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
+.RE
+.PP
SIGHUP
.RS 4
Sending a