+Changes in 2.2beta2
+====================
+
+* NEW: primary reconnect
+
Changes in 2.2beta1
====================
}
#endif /* no afp/asp */
-static int dsi_start(AFPConfig *config, AFPConfig *configs,
- server_child *server_children)
+static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
+ server_child *server_children)
{
- DSI *dsi;
+ DSI *dsi = config->obj.handle;
+ afp_child_t *child = NULL;
- if (!(dsi = dsi_getsession(config->obj.handle, server_children,
- config->obj.options.tickleval))) {
- LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
- exit( EXITERR_CLNT );
+ if (!(child = dsi_getsession(dsi,
+ server_children,
+ config->obj.options.tickleval))) {
+ LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
+ return NULL;
}
/* we've forked. */
- if (dsi->child) {
+ if (parent_or_child == 1) {
configfree(configs, config);
+ LOG(log_error, logtype_afpd, "dsi_start: IPC fd: %i", child->ipc_fds[1]);
+ config->obj.ipc_fd = child->ipc_fds[1];
+ close(child->ipc_fds[0]); /* Close parent IPC fd */
+ free(child);
afp_over_dsi(&config->obj); /* start a session */
exit (0);
}
- return 0;
+ return child;
}
#ifndef NO_DDP
unsigned char *optcount;
char status[1400];
const void *defoptions, *signature;
- int (*server_start) (struct AFPConfig *, struct AFPConfig *,
+ afp_child_t *(*server_start) (struct AFPConfig *, struct AFPConfig *,
server_child *);
void (*server_cleanup) (const struct AFPConfig *);
struct AFPConfig *next;
#include "uid.h"
#endif /* FORCE_UIDGID */
-#define CHILD_DIE (1 << 0)
-#define CHILD_RUNNING (1 << 1)
-#define CHILD_SLEEPING (1 << 2)
-#define CHILD_DATA (1 << 3)
+#define CHILD_DIE (1 << 0)
+#define CHILD_RUNNING (1 << 1)
+#define CHILD_SLEEPING (1 << 2)
+#define CHILD_DATA (1 << 3)
+#define CHILD_DISCONNECTED (1 << 4)
/*
* We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
{
DSI *dsi = obj->handle;
+ close(obj->ipc_fd);
+ obj->ipc_fd = -1;
+
/* we may have been called from a signal handler caught when afpd was running
* as uid 0, that's the wrong user for volume's prexec_close scripts if any,
* restore our login user
}
}
+static void afp_dsi_transfer_session(int sig _U_)
+{
+ uint16_t dsiID;
+ int socket;
+ DSI *dsi = (DSI *)child.obj->handle;
+
+ LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session fd");
+
+ if (readt(child.obj->ipc_fd, &dsiID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye");
+ afp_dsi_close(child.obj);
+ exit(EXITERR_SYS);
+ }
+
+ if ((socket = recv_fd(child.obj->ipc_fd, 1)) == -1) {
+ LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+ afp_dsi_close(child.obj);
+ exit(EXITERR_SYS);
+ }
+
+ close(dsi->socket);
+ dsi->socket = socket;
+ dsi->header.dsi_requestID = dsiID;
+ dsi->header.dsi_len = 0;
+ dsi->header.dsi_code = AFP_OK;
+ dsi->header.dsi_command = DSIFUNC_CMD;
+ dsi->header.dsi_flags = DSIFL_REPLY;
+
+ if (!dsi_cmdreply(dsi, AFP_OK)) {
+ LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+ afp_dsi_close(child.obj);
+ exit(EXITERR_CLNT);
+ }
+
+
+ LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect");
+ /*
+ * Now returning from this signal handler return to dsi_receive which should start
+ * reading/continuing from the connected socket that was passed via the parent from
+ * another session. The parent will terminate that session.
+ */
+}
+
/* */
static void afp_dsi_sleep(void)
{
int err;
DSI *dsi = (DSI *) child.obj->handle;
- /* we have to restart the timer because some libraries
- * may use alarm() */
+ if (child.flags & CHILD_DISCONNECTED) {
+ LOG(log_note, logtype_afpd, "afp_alarm: no reconnect within 10 hours, goodbye");
+ afp_dsi_die(EXITERR_CLNT);
+ }
+
+ /* we have to restart the timer because some libraries may use alarm() */
setitimer(ITIMER_REAL, &dsi->timer, NULL);
/* we got some traffic from the client since the previous timer
* tick. */
if ((child.flags & CHILD_DATA)) {
child.flags &= ~CHILD_DATA;
- return;
+ child.flags &= ~CHILD_DISCONNECTED;
+ return;
}
/* if we're in the midst of processing something,
err = dsi_tickle(child.obj->handle);
if (err <= 0)
afp_dsi_die(EXITERR_CLNT);
-
- } else { /* didn't receive a tickle. close connection */
- LOG(log_error, logtype_afpd, "afp_alarm: child timed out");
- afp_dsi_die(EXITERR_CLNT);
+ } else { /* didn't receive a tickle, enter disconnected state */
+ LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
+ struct itimerval t = {{0, 60 * 60 * 10}, {0, 0}}; /* 10 hours */
+ setitimer(ITIMER_REAL, &t, NULL);
+ child.flags |= CHILD_DISCONNECTED;
}
}
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);
+#ifdef SERVERTEXT
+ sigaddset(&action.sa_mask, SIGUSR2);
+#endif
+ 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 );
token = obj->sinfo.sessiontoken;
}
break;
- case 3: /* Jaguar */
+ case 3:
case 4:
if (ibuflen >= 8 ) {
p = ibuf;
if (ibuflen < idlen || idlen > (90-10)) {
return AFPERR_PARAM;
}
- server_ipc_write(IPC_GETSESSION, idlen+8, p );
+ ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
tklen = obj->sinfo.sessiontoken_len;
token = obj->sinfo.sessiontoken;
}
}
/* ---------------------- */
-int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_disconnect(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
+ DSI *dsi = (DSI *)obj->handle;
u_int16_t type;
-
u_int32_t tklen;
pid_t token;
int i;
}
}
- /* killed old session, not easy */
- server_ipc_write(IPC_KILLTOKEN, tklen, &token);
- sleep(1);
+ LOG(log_note, logtype_afpd, "afp_disconnect: trying primary reconnect");
- return AFPERR_SESSCLOS; /* was AFP_OK */
+ /* check for old session, possibly transfering session from here to there */
+ if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) == -1)
+ goto exit;
+ /* write uint16_t DSI request ID */
+ if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_afpd, "afp_disconnect: couldn't send DSI request ID");
+ goto exit;
+ }
+ /* now send our connected AFP client socket */
+ if (send_fd(obj->ipc_fd, dsi->socket) != 0)
+ goto exit;
+ /* Now see what happens: either afpd master kills us because our session */
+ /* has been transfered to a old disconnected session, or we continue */
+ sleep(2);
+
+exit:
+ LOG(log_error, logtype_afpd, "afp_disconnect: primary reconnect failed");
+ return AFPERR_MISC;
}
/* ---------------------- */
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 */
#ifdef FORCE_UIDGID
int force_uid;
uidgidset uidgid;
#include <atalk/logger.h>
#include <sys/time.h>
#include <sys/socket.h>
-
+#include <sys/poll.h>
#include <errno.h>
+#include <sys/wait.h>
#include <atalk/adouble.h>
struct afp_options default_options;
static AFPConfig *configs;
static server_child *server_children;
-static fd_set save_rfds;
-static int Ipc_fd = -1;
static sig_atomic_t reloadconfig = 0;
+/* Two pointers to dynamic allocated arrays which store pollfds and associated data */
+static struct pollfd *fdset;
+static struct polldata *polldata;
+static int fdset_size; /* current allocated size */
+static int fdset_used; /* number of used elements */
+
+
#ifdef TRU64
void afp_get_cmdline( int *ac, char ***av)
{
}
#endif /* TRU64 */
-static void afp_exit(const int i)
+/* This is registered with atexit() */
+static void afp_exit(void)
{
- server_unlock(default_options.pidfile);
- exit(i);
+ if (parent_or_child == 0)
+ /* Only do this in the parent */
+ server_unlock(default_options.pidfile);
}
+
/* ------------------
initialize fd set we are waiting for.
*/
-static void set_fd(int ipc_fd)
+static void fd_set_listening_sockets(void)
{
AFPConfig *config;
- FD_ZERO(&save_rfds);
for (config = configs; config; config = config->next) {
if (config->fd < 0) /* for proxies */
continue;
- FD_SET(config->fd, &save_rfds);
- }
- if (ipc_fd >= 0) {
- FD_SET(ipc_fd, &save_rfds);
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
}
}
+static void fd_reset_listening_sockets(void)
+{
+ AFPConfig *config;
+
+ for (config = configs; config; config = config->next) {
+ if (config->fd < 0) /* for proxies */
+ continue;
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
+ }
+ fd_set_listening_sockets();
+}
+
/* ------------------ */
static void afp_goaway(int sig)
{
asp_kill(sig);
#endif /* ! NO_DDP */
- dsi_kill(sig);
+ if (server_children)
+ server_child_kill(server_children, CHILD_DSIFORK, sig);
+
switch( sig ) {
case SIGTERM :
for (config = configs; config; config = config->next)
if (config->server_cleanup)
config->server_cleanup(config);
- afp_exit(0);
+ server_unlock(default_options.pidfile);
+ exit(0);
break;
case SIGUSR1 :
static void child_handler(int sig _U_)
{
- server_child_handler(server_children);
+ int fd;
+ int status, i;
+ pid_t pid;
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif /* ! WAIT_ANY */
+
+ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+ for (i = 0; i < server_children->nforks; i++) {
+ if ((fd = server_child_remove(server_children, i, pid)) != -1) {
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);
+ break;
+ }
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+ else
+ LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+ } else {
+ if (WIFSIGNALED(status))
+ LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+ else
+ LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+ }
+ }
}
int main(int ac, char **av)
default: /* server */
exit(0);
}
+ atexit(afp_exit);
/* install child handler for asp and dsi. we do this before afp_goaway
* as afp_goaway references stuff from here.
if (!(server_children = server_child_alloc(default_options.connections,
CHILD_NFORKS))) {
LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
memset(&sv, 0, sizeof(sv));
sigemptyset( &sv.sa_mask );
if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
#endif
#endif
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sv.sa_handler = afp_goaway;
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sigemptyset( &sv.sa_mask );
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
/* afpd.conf: not in config file: lockfile, connections, configfile
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
if (!(configs = configinit(&default_options))) {
LOG(log_error, logtype_afpd, "main: no servers configured");
- afp_exit(EXITERR_CONF);
+ exit(EXITERR_CONF);
}
pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
cnid_init();
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
- if ((ipc = server_ipc_create())) {
- Ipc_fd = server_ipc_parent(ipc);
- }
- set_fd(Ipc_fd);
+ fd_set_listening_sockets();
+
+ afp_child_t *child;
/* wait for an appleshare connection. parent remains in the loop
* while the children get handled by afp_over_{asp,dsi}. this is
* afterwards. establishing timeouts for logins is a possible
* solution. */
while (1) {
- rfds = save_rfds;
+ LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
- ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
+ ret = poll(fdset, fdset_used, -1);
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
int saveerrno = errno;
if (reloadconfig) {
nologin++;
auth_unload();
- AFPConfig *config;
LOG(log_info, logtype_afpd, "re-reading configuration file");
for (config = configs; config; config = config->next)
configfree(configs, NULL);
if (!(configs = configinit(&default_options))) {
LOG(log_error, logtype_afpd, "config re-read: no servers configured");
- afp_exit(EXITERR_CONF);
+ exit(EXITERR_CONF);
}
- set_fd(Ipc_fd);
+ fd_reset_listening_sockets();
nologin = 0;
reloadconfig = 0;
errno = saveerrno;
}
+
+ if (ret == 0)
+ continue;
if (ret < 0) {
if (errno == EINTR)
LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
break;
}
- if (Ipc_fd >=0 && FD_ISSET(Ipc_fd, &rfds)) {
- server_ipc_read(server_children);
- }
- for (config = configs; config; config = config->next) {
- if (config->fd < 0)
- continue;
- if (FD_ISSET(config->fd, &rfds)) {
- config->server_start(config, configs, server_children);
- }
- }
- }
+
+ for (int i = 0; i < fdset_used; i++) {
+ if (fdset[i].revents & POLLIN) {
+ switch (polldata[i].fdtype) {
+ case LISTEN_FD:
+ config = (AFPConfig *)polldata[i].data;
+ /* config->server_start is afp_config.c:dsi_start() for DSI */
+ if (child = config->server_start(config, configs, server_children)) {
+ /* Add IPC fd to select fd set */
+ 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;
+ }
+ break;
+ default:
+ LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
+ break;
+ } /* switch */
+ } /* if */
+ } /* for (i)*/
+ } /* while (1) */
return 0;
}
int afpmaster = 0;
int xlatevolname = 0;
- if (! ((DSI *)obj->handle)->child)
+ if (parent_or_child == 0)
afpmaster = 1;
if (path && !volname)
/* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
allow -> either no list (-1), or in list (1)
deny -> either no list (-1), or not in list (0) */
- if (!((DSI *)obj->handle)->child
+ if (parent_or_child == 0
||
(accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
(accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
* 1) neither the rolist nor the rwlist exist -> rw
* 2) rolist exists -> ro if user is in it.
* 3) rwlist exists -> ro unless user is in it. */
- if (((DSI *)obj->handle)->child
+ if (parent_or_child == 1
&&
((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0)
&&
free_volumes();
}
- if (! ((DSI *)obj->handle)->child) {
+ if (parent_or_child == 0) {
LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
} else {
LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
#include <sys/socket.h>
#include <stdio.h>
#include <time.h>
-#include <sys/ioctl.h>
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
return NULL;
}
-/* -------------------- */
-static int send_cred(int socket, int fd)
-{
- int ret;
- struct msghdr msgh;
- struct iovec iov[1];
- struct cmsghdr *cmsgp = NULL;
- char *buf;
- size_t size;
- int er=0;
-
- size = CMSG_SPACE(sizeof fd);
- buf = malloc(size);
- if (!buf) {
- LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
- return -1;
- }
-
- memset(&msgh,0,sizeof (msgh));
- memset(buf,0, size);
-
- msgh.msg_name = NULL;
- msgh.msg_namelen = 0;
-
- msgh.msg_iov = iov;
- msgh.msg_iovlen = 1;
-
- iov[0].iov_base = &er;
- iov[0].iov_len = sizeof(er);
-
- msgh.msg_control = buf;
- msgh.msg_controllen = size;
-
- cmsgp = CMSG_FIRSTHDR(&msgh);
- cmsgp->cmsg_level = SOL_SOCKET;
- cmsgp->cmsg_type = SCM_RIGHTS;
- cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
-
- *((int *)CMSG_DATA(cmsgp)) = fd;
- msgh.msg_controllen = cmsgp->cmsg_len;
-
- do {
- ret = sendmsg(socket,&msgh, 0);
- } while ( ret == -1 && errno == EINTR );
- if (ret == -1) {
- LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
- free(buf);
- return -1;
- }
- free(buf);
- return 0;
-}
-
/* -------------------- */
static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo)
{
up = test_usockfn(volinfo);
if (up && up->pid) {
/* we already have a process, send our fd */
- if (send_cred(up->control_fd, rqstfd) < 0) {
+ if (send_fd(up->control_fd, rqstfd) < 0) {
/* FIXME */
return -1;
}
return;
}
-static int recv_cred(int fd)
-{
- int ret;
- struct msghdr msgh;
- struct iovec iov[1];
- struct cmsghdr *cmsgp = NULL;
- char buf[CMSG_SPACE(sizeof(int))];
- char dbuf[80];
-
- memset(&msgh,0,sizeof(msgh));
- memset(buf,0,sizeof(buf));
-
- msgh.msg_name = NULL;
- msgh.msg_namelen = 0;
-
- msgh.msg_iov = iov;
- msgh.msg_iovlen = 1;
-
- iov[0].iov_base = dbuf;
- iov[0].iov_len = sizeof(dbuf);
-
- msgh.msg_control = buf;
- msgh.msg_controllen = sizeof(buf);
-
- do {
- ret = recvmsg(fd ,&msgh,0);
- } while ( ret == -1 && errno == EINTR );
-
- if ( ret == -1 ) {
- return -1;
- }
-
- for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
- if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
- return *(int *) CMSG_DATA(cmsgp);
- }
- }
-
- if ( ret == sizeof (int) )
- errno = *(int *)dbuf; /* Rcvd errno */
- else
- errno = ENOENT; /* Default errno */
-
- return -1;
-}
/*
* Check for client requests. We keep up to fd_table_size open descriptors in
if (FD_ISSET(control_fd, &readfds)) {
int l = 0;
- fd = recv_cred(control_fd);
+ fd = recv_fd(control_fd, 0);
if (fd < 0) {
return -1;
}
size_t datalen, cmdlen;
off_t read_count, write_count;
int asleep; /* client won't reply AFP 0x7a ? */
- /* inited = initialized?, child = a child?, noreply = send reply? */
- char child, inited, noreply;
+ /* noreply = send reply? */
+ char noreply;
const char *program;
int socket, serversock;
extern void dsi_setstatus (DSI *, char *, const size_t);
/* in dsi_getsess.c */
-extern DSI *dsi_getsession (DSI *, server_child *, const int);
+extern afp_child_t *dsi_getsession (DSI *, server_child *, const int);
extern void dsi_kill (int);
int count, nsessions, nforks;
} server_child;
+typedef struct server_child_data {
+ 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 */
+ uint32_t idlen; /* clientid len (from the Mac client) */
+ char *clientid; /* clientid (from the Mac client) */
+ int ipc_fds[2]; /* socketpair for IPC bw */
+ struct server_child_data **prevp, *next;
+} afp_child_t;
+
+extern int parent_or_child;
+
/* server_child.c */
extern server_child *server_child_alloc (const int, const int);
-extern int server_child_add (server_child *, const int, const pid_t);
-extern int server_child_remove (server_child *, const int, const pid_t);
+extern afp_child_t *server_child_add (server_child *, int, pid_t, uint ipc_fds[2]);
+extern int server_child_remove (server_child *, const int, const pid_t);
extern void server_child_free (server_child *);
extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one (server_child *children, const int forkid, const pid_t, const uid_t);
extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
const u_int32_t len, char *id, u_int32_t boottime);
-
+extern int server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
extern void server_child_handler (server_child *);
extern void server_reset_signal (void);
+#ifndef ATALK_SERVER_IPC_H
+#define ATALK_SERVER_IPC_H
#include <atalk/server_child.h>
-#define IPC_KILLTOKEN 1
-#define IPC_GETSESSION 2
-
-void *server_ipc_create(void);
-int server_ipc_child(void *obj);
-int server_ipc_parent(void *obj);
-int server_ipc_read(server_child *children);
-int server_ipc_write(u_int16_t command, int len, void *token);
-
-
-
+#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);
+#endif /* IPC_GETSESSION_LOGIN */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
+#include <poll.h>
#include <netatalk/at.h>
#include <atalk/unicode.h>
extern int setnonblock(int fd, int cmd);
extern ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout);
+extern ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout);
extern const char *getip_string(const struct sockaddr *sa);
extern unsigned int getip_port(const struct sockaddr *sa);
extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
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};
+struct polldata {
+ enum fdtype fdtype; /* IPC fd or listening socket fd */
+ void *data; /* pointer to AFPconfig for listening socket and *
+ * pointer to afp_child_t for IPC fd */
+};
+
+extern void fdset_add_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd,
+ enum fdtype fdtype,
+ void *data);
+extern void fdset_del_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd);
+extern int send_fd(int socket, int fd);
+extern int recv_fd(int fd, int nonblocking);
+
/******************************************************************
* unix.c
*****************************************************************/
&(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL)
return NULL;
+ int dummy[2];
switch ((pid = fork())) {
case 0 : /* child */
server_reset_signal();
break;
default : /* parent process */
- /* we need atomic setting or pb with tickle_handler
- */
- switch (server_child_add(children, CHILD_ASPFORK, pid)) {
- case 0: /* added child */
- if ((asp_ac_tmp = (struct asp_child *)
- malloc(sizeof(struct asp_child)))) {
- asp_ac_tmp->ac_pid = pid;
- asp_ac_tmp->ac_state = ACSTATE_OK;
- asp_ac_tmp->ac_sat = sat;
- asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
-
- asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
- asp->cmdbuf[1] = sid;
- set_asp_ac(sid, asp_ac_tmp);
- asperr = ASPERR_OK;
- break;
- } /* fall through if malloc fails */
- case -1: /* bad error */
+ /* we need atomic setting or pb with tickle_handler */
+ if (server_child_add(children, CHILD_ASPFORK, pid, dummy)) {
+ if ((asp_ac_tmp = malloc(sizeof(struct asp_child))) == NULL) {
+ kill(pid, SIGQUIT);
+ break;
+ }
+ asp_ac_tmp->ac_pid = pid;
+ asp_ac_tmp->ac_state = ACSTATE_OK;
+ asp_ac_tmp->ac_sat = sat;
+ asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
+
+ asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+ asp->cmdbuf[1] = sid;
+ set_asp_ac(sid, asp_ac_tmp);
+ asperr = ASPERR_OK;
+ break;
+ } else {
kill(pid, SIGQUIT);
break;
- default: /* non-fatal error */
- break;
- }
+ }
atp_close(atp);
break;
}
#include <atalk/dsi.h>
#include <atalk/server_child.h>
-static server_child *children = NULL;
-
-void dsi_kill(int sig)
-{
- if (children)
- server_child_kill(children, CHILD_DSIFORK, sig);
-}
-
/* hand off the command. return child connection to the main program */
-DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
- const int tickleval)
+afp_child_t *dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval)
{
pid_t pid;
-
- /* do a couple things on first entry */
- if (!dsi->inited) {
- if (!(children = serv_children))
- return NULL;
- dsi->inited = 1;
+ unsigned int ipc_fds[2];
+ afp_child_t *child;
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
+ LOG(log_error, logtype_afpd, "dsi_getsess: %s", strerror(errno));
+ exit( EXITERR_CLNT );
}
-
- switch (pid = dsi->proto_open(dsi)) {
+
+ if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) {
+ LOG(log_error, logtype_afpd, "dsi_getsess: setnonblock: %s", strerror(errno));
+ exit(EXITERR_CLNT);
+ }
+
+ switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */
case -1:
/* if we fail, just return. it might work later */
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
- return dsi;
+ return NULL;
case 0: /* child. mostly handled below. */
- dsi->child = 1;
break;
default: /* parent */
/* using SIGQUIT is hokey, but the child might not have
* re-established its signal handler for SIGTERM yet. */
- if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+ if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = DSIERR_SERVBUSY;
dsi->header.dsi_code = DSIERR_OK;
kill(pid, SIGQUIT);
}
-
dsi->proto_close(dsi);
- return dsi;
+ return child;
}
/* child: check number of open connections. this is one off the
* actual count. */
- if ((children->count >= children->nsessions) &&
+ if ((serv_children->count >= serv_children->nsessions) &&
(dsi->header.dsi_command == DSIFUNC_OPEN)) {
LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
dsi->header.dsi_flags = DSIFL_REPLY;
/* get rid of some stuff */
close(dsi->serversock);
- server_child_free(children);
- children = NULL;
+ server_child_free(serv_children);
switch (dsi->header.dsi_command) {
case DSIFUNC_STAT: /* send off status and return */
dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
dsi_opensession(dsi);
- return dsi;
+ if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+ exit(EXITERR_SYS);
+ child->ipc_fds[1] = ipc_fds[1];
+ return child;
break;
default: /* just close */
u_int8_t block[DSI_BLOCKSIZ];
size_t stored;
+ /* Immediateyl mark globally that we're a child now */
+ parent_or_child = 1;
+
/* reset signals */
server_reset_signal();
/*
- * $Id: server_child.c,v 1.12 2010-01-21 14:14:49 didg Exp $
- *
* Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
* All rights reserved. See COPYRIGHT.
*
*
- * handle inserting, removing, and freeing of children.
+ * handle inserting, removing, and freeing of children.
* this does it via a hash table. it incurs some overhead over
* a linear append/remove in total removal and kills, but it makes
* single-entry removals a fast operation. as total removals occur during
- * child initialization and kills during server shutdown, this is
+ * child initialization and kills during server shutdown, this is
* probably a win for a lot of connections and unimportant for a small
* number of connections.
*/
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <signal.h>
-#include <atalk/logger.h>
+#include <errno.h>
/* POSIX.1 sys/wait.h check */
#include <sys/types.h>
#endif /* HAVE_SYS_WAIT_H */
#include <sys/time.h>
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif /* ! WEXITSTATUS */
#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#endif
#ifndef WIFSIGNALED
-#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
#endif
#ifndef WTERMSIG
#define WTERMSIG(status) ((status) & 0x7f)
#endif
-#include <atalk/server_child.h>
-
/* hash/child functions: hash OR's pid */
#define CHILD_HASHSIZE 32
#define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
-struct server_child_data {
- 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 */
- u_int32_t time; /* client boot time (from the mac client) */
- int killed; /* 1 if we already tried to kill the client */
-
- u_int32_t idlen; /* clientid len (from the Mac client) */
- char *clientid; /* clientid (from the Mac client) */
- struct server_child_data **prevp, *next;
-};
-
typedef struct server_child_fork {
- struct server_child_data *table[CHILD_HASHSIZE];
- void (*cleanup)(const pid_t);
+ struct server_child_data *table[CHILD_HASHSIZE];
+ void (*cleanup)(const pid_t);
} server_child_fork;
-
+int parent_or_child; /* 0: parent, 1: child */
+
static inline void hash_child(struct server_child_data **htable,
- struct server_child_data *child)
+ struct server_child_data *child)
{
- struct server_child_data **table;
+ struct server_child_data **table;
- table = &htable[HASH(child->pid)];
- if ((child->next = *table) != NULL)
- (*table)->prevp = &child->next;
- *table = child;
- child->prevp = table;
+ table = &htable[HASH(child->pid)];
+ if ((child->next = *table) != NULL)
+ (*table)->prevp = &child->next;
+ *table = child;
+ child->prevp = table;
}
static inline void unhash_child(struct server_child_data *child)
{
- if (child->prevp) {
- if (child->next)
- child->next->prevp = child->prevp;
- *(child->prevp) = child->next;
- }
+ if (child->prevp) {
+ if (child->next)
+ child->next->prevp = child->prevp;
+ *(child->prevp) = child->next;
+ }
}
-static inline struct server_child_data
-*resolve_child(struct server_child_data **table, const pid_t pid)
+static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
{
- struct server_child_data *child;
+ struct server_child_data *child;
- for (child = table[HASH(pid)]; child; child = child->next) {
- if (child->pid == pid)
- break;
- }
+ for (child = table[HASH(pid)]; child; child = child->next) {
+ if (child->pid == pid)
+ break;
+ }
- return child;
+ return child;
}
/* initialize server_child structure */
server_child *server_child_alloc(const int connections, const int nforks)
{
- server_child *children;
+ server_child *children;
- children = (server_child *) calloc(1, sizeof(server_child));
- if (!children)
- return NULL;
+ children = (server_child *) calloc(1, sizeof(server_child));
+ if (!children)
+ return NULL;
- children->nsessions = connections;
- children->nforks = nforks;
- children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
-
- if (!children->fork) {
- free(children);
- return NULL;
- }
+ children->nsessions = connections;
+ children->nforks = nforks;
+ children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+
+ if (!children->fork) {
+ free(children);
+ return NULL;
+ }
- return children;
+ return children;
}
-/* add a child in. return 0 on success, -1 on serious error, and
- * > 0 for a non-serious error. */
-int server_child_add(server_child *children, const int forkid,
- const pid_t pid)
+/*!
+ * add a child
+ * @return pointer to struct server_child_data on success, NULL on error
+ */
+afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, uint ipc_fds[2])
{
- server_child_fork *fork;
- struct server_child_data *child;
- sigset_t sig, oldsig;
-
- /* we need to prevent deletions from occuring before we get a
- * chance to add the child in. */
- sigemptyset(&sig);
- sigaddset(&sig, SIGCHLD);
- pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
-
- /* it's possible that the child could have already died before the
- * pthread_sigmask. we need to check for this. */
- if (kill(pid, 0) < 0) {
- pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
- return 1;
- }
+ server_child_fork *fork;
+ afp_child_t *child = NULL;
+ sigset_t sig, oldsig;
- fork = (server_child_fork *) children->fork + forkid;
+ /* we need to prevent deletions from occuring before we get a
+ * chance to add the child in. */
+ sigemptyset(&sig);
+ sigaddset(&sig, SIGCHLD);
+ pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
- /* if we already have an entry. just return. */
- if (resolve_child(fork->table, pid)) {
- pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
- return 0;
- }
+ /* it's possible that the child could have already died before the
+ * pthread_sigmask. we need to check for this. */
+ if (kill(pid, 0) < 0)
+ goto exit;
- if ((child = (struct server_child_data *)
- calloc(1, sizeof(struct server_child_data))) == NULL) {
- pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
- return -1;
- }
+ fork = (server_child_fork *) children->fork + forkid;
- child->pid = pid;
- child->valid = 0;
- child->killed = 0;
- hash_child(fork->table, child);
- children->count++;
- pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+ /* if we already have an entry. just return. */
+ if (child = resolve_child(fork->table, pid))
+ goto exit;
- return 0;
+ if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+ goto exit;
+
+ child->pid = pid;
+ child->valid = 0;
+ child->killed = 0;
+ child->ipc_fds[0] = ipc_fds[0];
+ child->ipc_fds[1] = ipc_fds[1];
+
+ hash_child(fork->table, child);
+ children->count++;
+
+exit:
+ pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+ return child;
}
/* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid,
- const pid_t pid)
+int server_child_remove(server_child *children, const int forkid, pid_t pid)
{
- server_child_fork *fork;
- struct server_child_data *child;
-
- fork = (server_child_fork *) children->fork + forkid;
- if (!(child = resolve_child(fork->table, pid)))
- return 0;
-
- unhash_child(child);
- if (child->clientid) {
- free(child->clientid);
- }
- free(child);
- children->count--;
- return 1;
+ int fd;
+ server_child_fork *fork;
+ struct server_child_data *child;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ if (!(child = resolve_child(fork->table, pid)))
+ return -1;
+
+ unhash_child(child);
+ if (child->clientid) {
+ free(child->clientid);
+ child->clientid = NULL;
+ }
+
+ /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
+ fd = child->ipc_fds[0];
+ if (child->ipc_fds[0] != -1) {
+ close(child->ipc_fds[0]);
+ child->ipc_fds[0] = -1;
+ }
+ if (child->ipc_fds[1] != -1) {
+ close(child->ipc_fds[1]);
+ child->ipc_fds[1] = -1;
+ }
+
+ free(child);
+ children->count--;
+
+ if (fork->cleanup)
+ fork->cleanup(pid);
+
+ return fd;
}
/* free everything: by using a hash table, this increases the cost of
* this part over a linked list by the size of the hash table */
void server_child_free(server_child *children)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i, j;
-
- for (i = 0; i < children->nforks; i++) {
- fork = (server_child_fork *) children->fork + i;
- for (j = 0; j < CHILD_HASHSIZE; j++) {
- child = fork->table[j]; /* start at the beginning */
- while (child) {
- tmp = child->next;
- if (child->clientid) {
- free(child->clientid);
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i, j;
+
+ for (i = 0; i < children->nforks; i++) {
+ fork = (server_child_fork *) children->fork + i;
+ for (j = 0; j < CHILD_HASHSIZE; j++) {
+ child = fork->table[j]; /* start at the beginning */
+ while (child) {
+ tmp = child->next;
+ if (child->clientid) {
+ free(child->clientid);
+ }
+ free(child);
+ child = tmp;
+ }
}
- free(child);
- child = tmp;
- }
}
- }
- free(children->fork);
- free(children);
+ free(children->fork);
+ free(children);
}
-/* send kill to child processes: this also has an increased cost over
- * a plain-old linked list */
-void server_child_kill(server_child *children, const int forkid,
- const int sig)
+/* send signal to all child processes */
+void server_child_kill(server_child *children, int forkid, int sig)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- kill(child->pid, sig);
- child = tmp;
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ for (i = 0; i < CHILD_HASHSIZE; i++) {
+ child = fork->table[i];
+ while (child) {
+ tmp = child->next;
+ kill(child->pid, sig);
+ child = tmp;
+ }
}
- }
}
/* send kill to a child processes.
- * a plain-old linked list
+ * a plain-old linked list
* FIXME use resolve_child ?
*/
static int kill_child(struct server_child_data *child)
{
- if (!child->killed) {
- kill(child->pid, SIGTERM);
- /* we don't wait because there's no guarantee that we can really kill it */
- child->killed = 1;
- return 1;
- }
- else {
- LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid);
- }
- return 0;
+ if (!child->killed) {
+ kill(child->pid, SIGTERM);
+ /* we don't wait because there's no guarantee that we can really kill it */
+ child->killed = 1;
+ return 1;
+ } else {
+ LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
+ kill(child->pid, SIGKILL);
+ }
+ return 1;
}
-/* -------------------- */
-void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
+/*!
+ * Try to find an old session and pass socket
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+int server_child_transfer_session(server_child *children,
+ int forkid,
+ pid_t pid,
+ uid_t uid,
+ int afp_socket,
+ uint16_t DSI_requestID)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- if (child->pid == pid) {
- if (!child->valid) {
- /* hmm, client 'guess' the pid, rogue? */
- LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid);
- }
- else if (child->uid != uid) {
- LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid);
- }
- else {
- kill_child(child);
- }
- }
- child = tmp;
+ EC_INIT;
+ server_child_fork *fork;
+ struct server_child_data *child;
+ int i;
+
+ 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);
+ return 0;
}
- }
+
+ if (!child->valid) {
+ /* hmm, client 'guess' the pid, rogue? */
+ LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
+ return 0;
+ } else if (child->uid != uid) {
+ LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
+ return 0;
+ }
+
+ LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
+
+ if (writet(child->ipc_fds[0], &DSI_requestID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
+ EC_STATUS(-1);
+ goto EC_CLEANUP;
+ }
+ EC_ZERO_LOG(send_fd(child->ipc_fds[0], afp_socket));
+ EC_ZERO_LOG(kill(pid, SIGURG));
+
+ EC_STATUS(1);
+
+EC_CLEANUP:
+ EC_EXIT;
}
/* see if there is a process for the same mac */
/* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
- const uid_t uid,
- const u_int32_t idlen, char *id, u_int32_t boottime)
+void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+ uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- if ( child->pid != pid) {
- if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
- if ( child->time != boottime ) {
- if (uid == child->uid) {
- if (kill_child(child)) {
- LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
- }
- }
- else {
- LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
- }
- }
- else if (child->killed) {
- /* there's case where a Mac close a connection and restart a new one before
- * the first is 'waited' by the master afpd process
- */
- LOG(log_info, logtype_default,
- "WARNING: connection (%d) killed but still there.", child->pid);
- }
- else {
- LOG(log_info, logtype_default,
- "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
- child->pid, pid);
- }
-
- }
- }
- else
- {
- child->time = boottime;
- /* free old token if any */
- if (child->clientid) {
- free(child->clientid);
- }
- child->uid = uid;
- child->valid = 1;
- child->idlen = idlen;
- child->clientid = id;
- LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
- }
- child = tmp;
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i;
+
+ fork = (server_child_fork *)children->fork + forkid;
+
+ for (i = 0; i < CHILD_HASHSIZE; i++) {
+ child = fork->table[i];
+ while (child) {
+ tmp = child->next;
+ if ( child->pid != pid) {
+ if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
+ if ( child->time != boottime ) {
+ if (uid == child->uid) {
+ kill_child(child);
+ LOG(log_note, logtype_default, "Disconnected child[%u], client rebooted.", child->pid);
+ } else {
+ LOG(log_note, logtype_default, "Session with different pid[%u]", child->pid);
+ }
+ }
+ if (child->killed)
+ kill_child(child); /* this will send SIGKILL */
+ LOG(log_note, logtype_default, "", child->pid, pid);
+ }
+ } else {
+ /* update childs own slot */
+ child->time = boottime;
+ if (child->clientid)
+ free(child->clientid);
+ LOG(log_debug, logtype_default, "Setting client ID for %d", child->pid);
+ child->uid = uid;
+ child->valid = 1;
+ child->idlen = idlen;
+ child->clientid = id;
+ }
+ child = tmp;
+ }
}
- }
}
/* for extra cleanup if necessary */
void server_child_setup(server_child *children, const int forkid,
- void (*fcn)(const pid_t))
+ void (*fcn)(const pid_t))
{
- server_child_fork *fork;
+ server_child_fork *fork;
- fork = (server_child_fork *) children->fork + forkid;
- fork->cleanup = fcn;
+ fork = (server_child_fork *) children->fork + forkid;
+ fork->cleanup = fcn;
}
-/* keep track of children. */
-void server_child_handler(server_child *children)
-{
- int status, i;
- pid_t pid;
-
-#ifndef WAIT_ANY
-#define WAIT_ANY (-1)
-#endif /* ! WAIT_ANY */
-
- while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
- for (i = 0; i < children->nforks; i++) {
- if (server_child_remove(children, i, pid)) {
- server_child_fork *fork;
-
- fork = (server_child_fork *) children->fork + i;
- if (fork->cleanup)
- fork->cleanup(pid);
- break;
- }
- }
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status)) {
- LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
- WEXITSTATUS(status));
- } else {
- LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
- }
- } else {
- if (WIFSIGNALED(status))
- {
- LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
- WTERMSIG (status));
- }
- else
- {
- LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
- }
- }
- }
-}
-
-/* ---------------------------
+/* ---------------------------
* reset children signals
-*/
+ */
void server_reset_signal(void)
{
struct sigaction sv;
memset(&sv, 0, sizeof(sv));
sv.sa_handler = SIG_DFL;
sigemptyset( &sv.sa_mask );
-
+
sigaction(SIGALRM, &sv, NULL );
sigaction(SIGHUP, &sv, NULL );
sigaction(SIGTERM, &sv, NULL );
sigaction(SIGUSR1, &sv, NULL );
sigaction(SIGCHLD, &sv, NULL );
-
+
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
sigaddset(&sigs, SIGHUP);
sigaddset(&sigs, SIGUSR1);
sigaddset(&sigs, SIGCHLD);
pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
-
+
}
/*
- * $Id: server_ipc.c,v 1.4 2010-01-21 14:14:49 didg Exp $
- *
* All rights reserved. See COPYRIGHT.
*
- *
- * ipc between parent and children.
+ * IPC over socketpair between parent and children.
*/
#ifdef HAVE_CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <signal.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
#include <atalk/logger.h>
+#include <atalk/util.h>
typedef struct ipc_header {
- u_int16_t command;
- pid_t child_pid;
- uid_t uid;
- u_int32_t len;
- char *msg;
-} ipc_header;
-
-static int pipe_fd[2];
-
-void *server_ipc_create(void)
-{
- if (pipe(pipe_fd)) {
- return NULL;
- }
- return &pipe_fd;
-}
-
-/* ----------------- */
-int server_ipc_child(void *obj _U_)
-{
- /* close input */
- close(pipe_fd[0]);
- return pipe_fd[1];
-}
+ uint16_t command;
+ pid_t child_pid;
+ uid_t uid;
+ uint32_t len;
+ char *msg;
+ int afp_socket;
+ uint16_t DSI_requestID;
+} ipc_header_t;
+
+static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
+ "IPC_GETSESSION"};
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
- return pipe_fd[0];
-}
-
-/* ----------------- */
-static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
+/*
+ * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
{
pid_t pid;
/* assume signals SA_RESTART set */
memcpy (&pid, ipc->msg, sizeof(pid_t));
- LOG(log_info, logtype_default, "child %d user %d disconnected", pid, ipc->uid);
- server_child_kill_one(children, CHILD_DSIFORK, pid, ipc->uid);
- return 0;
+ return server_child_transfer_session(children,
+ CHILD_DSIFORK,
+ pid,
+ ipc->uid,
+ ipc->afp_socket,
+ ipc->DSI_requestID);
}
/* ----------------- */
-static int ipc_get_session (struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child *children)
{
u_int32_t boottime;
u_int32_t idlen;
char *clientid, *p;
+
+ if (ipc->len < (sizeof(idlen) + sizeof(boottime)) )
+ return -1;
- if (ipc->len < (sizeof(idlen) + sizeof(boottime)) ) {
- return -1;
- }
p = ipc->msg;
memcpy (&idlen, p, sizeof(idlen));
idlen = ntohl (idlen);
memcpy (&boottime, p, sizeof(boottime));
p += sizeof(boottime);
- if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime)) {
- return -1;
- }
- if (NULL == (clientid = (char*) malloc(idlen)) ) {
- return -1;
- }
+ if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime))
+ return -1;
+
+ if (NULL == (clientid = (char*) malloc(idlen)) )
+ return -1;
memcpy (clientid, p, idlen);
- server_child_kill_one_by_id (children, CHILD_DSIFORK, ipc->child_pid, ipc->uid, idlen, clientid, boottime);
- /* FIXME byte to ascii if we want to log clientid */
- LOG (log_debug, logtype_afpd, "ipc_get_session: len: %u, idlen %d, time %x", ipc->len, idlen, boottime);
+ LOG (log_debug, logtype_afpd, "ipc_get_session(pid: %u, uid: %u, time %x)",
+ ipc->child_pid, ipc->uid, boottime);
+
+ server_child_kill_one_by_id(children,
+ CHILD_DSIFORK,
+ ipc->child_pid,
+ ipc->uid,
+ idlen,
+ clientid,
+ boottime);
+
return 0;
}
* uid
*
*/
-int server_ipc_read(server_child *children)
+
+/*!
+ * Read a IPC message from a child
+ *
+ * @args children (rw) pointer to our structure with all childs
+ * @args fd (r) IPC socket with child
+ *
+ * @returns number of bytes transfered, -1 on error, 0 on EOF
+ */
+int ipc_server_read(server_child *children, int fd)
{
int ret = 0;
struct ipc_header ipc;
char buf[IPC_MAXMSGSIZE], *p;
- if ((ret = read(pipe_fd[0], buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
- LOG (log_info, logtype_afpd, "Reading IPC header failed (%u of %u bytes read)", ret, IPC_HEADERLEN);
- return -1;
- }
+ if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
+ LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s",
+ ret, IPC_HEADERLEN, strerror(errno));
+ return ret;
+ }
p = buf;
memcpy(&ipc.len, p, sizeof(ipc.len));
/* This should never happen */
- if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN))
- {
- LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
- return -1;
+ if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) {
+ LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
+ return -1;
}
memset (buf, 0, IPC_MAXMSGSIZE);
if ( ipc.len != 0) {
- if ((ret = read(pipe_fd[0], buf, ipc.len)) != (int) ipc.len) {
- LOG (log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read)", ret, ipc.len);
- return -1;
+ if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) {
+ LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read): %s",
+ ret, ipc.len, strerror(errno));
+ return ret;
}
}
ipc.msg = buf;
-
- LOG (log_debug, logtype_afpd, "ipc_read: command: %u, pid: %u, len: %u", ipc.command, ipc.child_pid, ipc.len);
- switch (ipc.command)
- {
- case IPC_KILLTOKEN:
- return (ipc_kill_token(&ipc, children));
- break;
+ LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
+ ipc_cmd_str[ipc.command], ipc.child_pid);
+
+ int afp_socket;
+
+ switch (ipc.command) {
+
+ case IPC_DISCOLDSESSION:
+ if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) {
+ LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s",
+ ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+ }
+ if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) {
+ LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s",
+ ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+ return -1;
+ }
+ if (ipc_kill_token(&ipc, children) == 1) {
+ /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */
+ LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer",
+ ipc.child_pid);
+ kill(ipc.child_pid, SIGKILL);
+ }
+ break;
+
case IPC_GETSESSION:
- return (ipc_get_session(&ipc, children));
- break;
+ if (ipc_get_session(&ipc, children) != 0)
+ return -1;
+ break;
+
default:
LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
return -1;
}
+ return ret;
}
/* ----------------- */
-int server_ipc_write( u_int16_t command, int len, void *msg)
+int ipc_child_write(int fd, uint16_t command, int len, void *msg)
{
char block[IPC_MAXMSGSIZE], *p;
pid_t pid;
memcpy(p, msg, len);
- LOG (log_debug, logtype_afpd, "ipc_write: command: %u, pid: %u, msglen: %u", command, pid, len);
- return write(pipe_fd[1], block, len+IPC_HEADERLEN );
+ LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
+
+ return write(fd, block, len+IPC_HEADERLEN );
}
#include <errno.h>
#include <sys/time.h>
#include <time.h>
+#include <sys/ioctl.h>
#include <atalk/logger.h>
+#include <atalk/util.h>
static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
return stored;
}
+/*!
+ * non-blocking drop-in replacement for read with timeout using select
+ *
+ * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1
+ * @param data (rw) buffer for the read data
+ * @param lenght (r) how many bytes to read
+ * @param setnonblocking (r) when non-zero this func will enable and disable non blocking
+ * io mode for the socket
+ * @param timeout (r) number of seconds to try reading
+ *
+ * @returns number of bytes actually read or -1 on fatal error
+ */
+ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
+{
+ size_t stored = 0;
+ ssize_t len = 0;
+ struct timeval now, end, tv;
+ fd_set rfds;
+ int ret;
+
+ if (setnonblocking) {
+ if (setnonblock(socket, 1) != 0)
+ return -1;
+ }
+
+ /* Calculate end time */
+ (void)gettimeofday(&now, NULL);
+ end = now;
+ end.tv_sec += timeout;
+
+ while (stored < length) {
+ len = write(socket, (char *) data + stored, length - stored);
+ if (len == -1) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EAGAIN:
+ FD_ZERO(&rfds);
+ FD_SET(socket, &rfds);
+ tv.tv_usec = 0;
+ tv.tv_sec = timeout;
+
+ while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+ switch (ret) {
+ case 0:
+ LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+ goto exit;
+
+ default: /* -1 */
+ if (errno == 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);
+ goto exit;
+ }
+ if (now.tv_usec > end.tv_usec) {
+ tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec - 1;
+ } else {
+ tv.tv_usec = end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec;
+ }
+ FD_ZERO(&rfds);
+ FD_SET(socket, &rfds);
+ continue;
+ }
+ LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ }
+ } /* while (select) */
+ continue;
+ } /* switch (errno) */
+ LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ } /* (len == -1) */
+ else if (len > 0)
+ stored += len;
+ else
+ break;
+ } /* while (stored < length) */
+
+exit:
+ if (setnonblocking) {
+ if (setnonblock(socket, 0) != 0)
+ return -1;
+ }
+
+ if (len == -1 && stored == 0)
+ /* last read or select got an error and we haven't got yet anything => return -1*/
+ return -1;
+ return stored;
+}
+
/*!
* @brief convert an IPv4 or IPv6 address to a static string using inet_ntop
*
* @param ai (rw) pointer to an struct sockaddr
* @parma mask (r) number of maskbits
*/
-void apply_ip_mask(struct sockaddr *sa, uint32_t mask)
+void apply_ip_mask(struct sockaddr *sa, int mask)
{
switch (sa->sa_family) {
return ret;
}
+
+#define POLL_FD_SET_STARTSIZE 512
+#define POLL_FD_SET_INCREASE 128
+/*!
+ * Add a fd to a dynamic pollfd array that is allocated and grown as needed
+ *
+ * This uses an additional array of struct polldata which stores type information
+ * (enum fdtype) and a pointer to anciliary user data.
+ *
+ * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
+ * *fdsetp is NULL.
+ * 2. Grow array as needed
+ * 3. Fill in both array elements and increase count of used elements
+ *
+ * @param fdsetp (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd (r) file descriptor to add to the arrays
+ * @param fdtype (r) type of fd, currently IPC_FD or LISTEN_FD
+ * @param data (rw) pointer to data the caller want to associate with an fd
+ */
+void fdset_add_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd,
+ enum fdtype fdtype,
+ void *data)
+{
+ struct pollfd *fdset = *fdsetp;
+ struct polldata *polldata = *polldatap;
+ int fdset_size = *fdset_sizep;
+
+ LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
+
+ if (fdset == NULL) { /* 1 */
+ /* Initialize with space for 512 fds */
+ fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
+ if (! fdset)
+ exit(EXITERR_SYS);
+
+ polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
+ if (! polldata)
+ exit(EXITERR_SYS);
+
+ fdset_size = 512;
+ *fdset_sizep = fdset_size;
+ *fdsetp = fdset;
+ *polldatap = polldata;
+ }
+
+ if (*fdset_usedp >= fdset_size) { /* 2 */
+ fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
+ if (fdset == NULL)
+ exit(EXITERR_SYS);
+
+ polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
+ if (polldata == NULL)
+ exit(EXITERR_SYS);
+
+ fdset_size += POLL_FD_SET_INCREASE;
+ *fdset_sizep = fdset_size;
+ *fdsetp = fdset;
+ *polldatap = polldata;
+ }
+
+ /* 3 */
+ fdset[*fdset_usedp].fd = fd;
+ fdset[*fdset_usedp].events = POLLIN;
+ polldata[*fdset_usedp].fdtype = fdtype;
+ polldata[*fdset_usedp].data = data;
+ (*fdset_usedp)++;
+}
+
+/*!
+ * Remove a fd from our pollfd array
+ *
+ * 1. Search fd
+ * 2. If we remove the last array elemnt, just decrease count
+ * 3. If found move all following elements down by one
+ * 4. Decrease count of used elements in array
+ *
+ * This currently doesn't shrink the allocated storage of the array.
+ *
+ * @param fdsetp (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd (r) file descriptor to remove from the arrays
+ */
+void fdset_del_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep _U_,
+ int fd)
+{
+ struct pollfd *fdset = *fdsetp;
+ struct polldata *polldata = *polldatap;
+
+ for (int i = 0; i < *fdset_usedp; i++) {
+ if (fdset[i].fd == fd) { /* 1 */
+ if (i < (*fdset_usedp - 1)) { /* 2 */
+ memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
+ memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
+ }
+ (*fdset_usedp)--;
+ break;
+ }
+ }
+}
+
+/*
+ * Receive a fd on a suitable socket
+ * @args fd (r) PF_UNIX socket to receive on
+ * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec
+ * @returns fd on success, -1 on error
+ */
+int recv_fd(int fd, int nonblocking)
+{
+ int ret;
+ struct msghdr msgh;
+ struct iovec iov[1];
+ struct cmsghdr *cmsgp = NULL;
+ char buf[CMSG_SPACE(sizeof(int))];
+ char dbuf[80];
+ struct pollfd pollfds[1];
+
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN;
+
+ memset(&msgh,0,sizeof(msgh));
+ memset(buf,0,sizeof(buf));
+
+ msgh.msg_name = NULL;
+ msgh.msg_namelen = 0;
+
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = 1;
+
+ iov[0].iov_base = dbuf;
+ iov[0].iov_len = sizeof(dbuf);
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = sizeof(buf);
+
+ if (nonblocking) {
+ do {
+ ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */
+ } while ( ret == -1 && errno == EINTR );
+ if (ret != 1)
+ return -1;
+ ret = recvmsg(fd, &msgh, 0);
+ } else {
+ do {
+ ret = recvmsg(fd, &msgh, 0);
+ } while ( ret == -1 && errno == EINTR );
+ }
+
+ if ( ret == -1 ) {
+ return -1;
+ }
+
+ for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
+ if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
+ return *(int *) CMSG_DATA(cmsgp);
+ }
+ }
+
+ if ( ret == sizeof (int) )
+ errno = *(int *)dbuf; /* Rcvd errno */
+ else
+ errno = ENOENT; /* Default errno */
+
+ return -1;
+}
+
+/*
+ * Send a fd across a suitable socket
+ */
+int send_fd(int socket, int fd)
+{
+ int ret;
+ struct msghdr msgh;
+ struct iovec iov[1];
+ struct cmsghdr *cmsgp = NULL;
+ char *buf;
+ size_t size;
+ int er=0;
+
+ size = CMSG_SPACE(sizeof fd);
+ buf = malloc(size);
+ if (!buf) {
+ LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&msgh,0,sizeof (msgh));
+ memset(buf,0, size);
+
+ msgh.msg_name = NULL;
+ msgh.msg_namelen = 0;
+
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = 1;
+
+ iov[0].iov_base = &er;
+ iov[0].iov_len = sizeof(er);
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = size;
+
+ cmsgp = CMSG_FIRSTHDR(&msgh);
+ cmsgp->cmsg_level = SOL_SOCKET;
+ cmsgp->cmsg_type = SCM_RIGHTS;
+ cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ *((int *)CMSG_DATA(cmsgp)) = fd;
+ msgh.msg_controllen = cmsgp->cmsg_len;
+
+ do {
+ ret = sendmsg(socket,&msgh, 0);
+ } while ( ret == -1 && errno == EINTR );
+ if (ret == -1) {
+ LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+ free(buf);
+ return -1;
+ }
+ free(buf);
+ return 0;
+}