+Changes in 2.2beta2
+====================
+
+* NEW: afpd: AFP 3.3
+* UPD: afpd: AFP 3.x can't be disabled
+
Changes in 2.2beta1
====================
================
* FIX: afpd: Fix for LDAP user cache corruption
+* FIX: afpd: Fix for not shown ACLs for when filesyem uid or gid
+ couldn't be resolved because (eg deleted users/groups)
* FIX: gentoo: cannot set $CNID_CONFIG
* FIX: ubuntu: servername was empty
* FIX: Solaris: configure script failed to enable DDP module
+* FIX: AppleDouble buffer overrun by extremely long filename
* UPD: afpd: return version info with machine type in DSIGetStatus
Changes in 2.1.5
]
)
-
-afp3=no
-afp3set=no
-AC_MSG_CHECKING([whether AFP 3.x calls should be enabled])
-AC_ARG_ENABLE(afp3,
- [ --disable-afp3 disable AFP 3.x calls],
- [
- if test x"$enableval" != x"no"; then
- afp3set=yes
- afp3=yes
- AC_MSG_RESULT([yes])
- else
- AC_MSG_RESULT([no])
- fi
- ],[
- AC_MSG_RESULT([yes])
- afp3=yes
- ]
-)
-
-if test x"$afp3" = x"yes"; then
- AC_SYS_LARGEFILE([
- AC_DEFINE(AFP3x, 1, [Define to enable AFP 3.x support])
- ],[
- if test x"$afp3set" = x"yes"; then
- AC_MSG_ERROR([AFP 3.x support requires Large File Support.])
- else
- AC_MSG_WARN([AFP 3.x support requires Large File Support. AFP3.x support disabled])
- afp3=no
- fi
- ])
-fi
-
+AC_SYS_LARGEFILE([], AC_MSG_ERROR([AFP 3.x support requires Large File Support.]))
AC_CHECK_ICONV
dnl ----------- A NOTE ABOUT DROPKLUDGE
/* Shall we return owner UUID ? */
if (bitmap & kFileSec_UUID) {
LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
- if (NULL == (pw = getpwuid(s_path->st.st_uid)))
- return AFPERR_MISC;
- LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
- if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
- return AFPERR_MISC;
+ if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
+ LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
+ localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
+ } else {
+ LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
+ if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+ return AFPERR_MISC;
+ }
rbuf += UUID_BINSIZE;
*rbuflen += UUID_BINSIZE;
}
/* Shall we return group UUID ? */
if (bitmap & kFileSec_GRPUUID) {
LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
- if (NULL == (gr = getgrgid(s_path->st.st_gid)))
- return AFPERR_MISC;
- LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
- if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
- return AFPERR_MISC;
+ if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
+ LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
+ localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
+ } else {
+ LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
+ if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+ return AFPERR_MISC;
+ }
rbuf += UUID_BINSIZE;
*rbuflen += UUID_BINSIZE;
}
}
#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);
+ 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)
-
/*
* We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
* available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
*/
AFPObj *AFPobj = NULL;
-static struct {
- AFPObj *obj;
- unsigned char flags;
- int tickle;
-} child;
+typedef struct {
+ uint16_t DSIreqID;
+ uint8_t AFPcommand;
+ uint32_t result;
+} rc_elem_t;
+/*
+ * AFP replay cache:
+ * - fix sized array
+ * - indexed just by taking DSIreqID mod REPLAYCACHE_SIZE
+ */
+rc_elem_t replaycache[REPLAYCACHE_SIZE];
static void afp_dsi_close(AFPObj *obj)
{
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
*/
in_handler = 1;
- dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
- afp_dsi_close(child.obj);
+ dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN);
+ afp_dsi_close(AFPobj);
if (sig) /* if no signal, assume dieing because logins are disabled &
don't log it (maintenance mode)*/
LOG(log_info, logtype_afpd, "Connection terminated");
}
}
+static void afp_dsi_transfer_session(int sig _U_)
+{
+ uint16_t dsiID;
+ int socket;
+ DSI *dsi = (DSI *)AFPobj->handle;
+
+ LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session");
+
+ if (readt(AFPobj->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(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) {
+ LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ if (dsi->socket != -1)
+ 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;
+
+ /*
+ * The session transfer happens in the middle of FPDisconnect old session, thus we
+ * have to send the reply now.
+ */
+ if (!dsi_cmdreply(dsi, AFP_OK)) {
+ LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+ afp_dsi_close(AFPobj);
+ 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)
{
- child.flags |= CHILD_SLEEPING;
- dsi_sleep(child.obj->handle, 1);
+ dsi_sleep(AFPobj->handle, 1);
}
/* ------------------- */
{
struct sigaction sv;
struct itimerval it;
-
- child.flags |= CHILD_DIE;
+ DSI *dsi = (DSI *)AFPobj->handle;
+ dsi->flags |= DSI_DIE;
/* shutdown and don't reconnect. server going down in 5 minutes. */
setmessage("The server is going down for maintenance.");
- if (dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ if (dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
AFPATTN_MESG | AFPATTN_TIME(5)) < 0) {
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
dsi->down_request = 1;
}
}
/* ---------------------- */
-#ifdef SERVERTEXT
static void afp_dsi_getmesg (int sig _U_)
{
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
dsi->msg_request = 1;
- if (dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
+ if (dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
dsi->msg_request = 2;
}
-#endif /* SERVERTEXT */
static void alarm_handler(int sig _U_)
{
int err;
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
- /* we have to restart the timer because some libraries
- * may use alarm() */
+ /* 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;
+ /* we got some traffic from the client since the previous timer tick. */
+ if ((dsi->flags & DSI_DATA)) {
+ dsi->flags &= ~DSI_DATA;
+ dsi->flags &= ~DSI_DISCONNECTED;
+ return;
}
- /* if we're in the midst of processing something,
- don't die. */
- if ((child.flags & CHILD_SLEEPING) && child.tickle++ < child.obj->options.sleep) {
+ dsi->tickle++;
+
+ if (dsi->flags & DSI_SLEEPING) {
+ if (dsi->tickle < AFPobj->options.sleep) {
+ LOG(log_error, logtype_afpd, "afp_alarm: sleep time ended");
+ afp_dsi_die(EXITERR_CLNT);
+ }
return;
}
-
- if ((child.flags & CHILD_RUNNING) || (child.tickle++ < child.obj->options.timeout)) {
- if (!(err = pollvoltime(child.obj)))
- err = dsi_tickle(child.obj->handle);
- if (err <= 0)
+
+ if (dsi->flags & DSI_DISCONNECTED) {
+ if (dsi->tickle > AFPobj->options.disconnected) {
+ LOG(log_error, logtype_afpd, "afp_alarm: no reconnect within 10 minutes, goodbye");
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);
+ }
+ return;
+ }
+
+ /* 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->flags |= DSI_DISCONNECTED;
+ return;
+ }
+
+ if ((err = pollvoltime(AFPobj)) == 0)
+ err = dsi_tickle(AFPobj->handle);
+ if (err <= 0) {
+ LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
+ dsi->flags |= DSI_DISCONNECTED;
}
}
if (dsi->msg_request) {
if (dsi->msg_request == 2) {
/* didn't send it in signal handler */
- dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
+ dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
}
dsi->msg_request = 0;
- readmessage(child.obj);
+ readmessage(AFPobj);
}
if (dsi->down_request) {
dsi->down_request = 0;
- dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
AFPATTN_MESG | AFPATTN_TIME(5));
}
}
void afp_over_dsi(AFPObj *obj)
{
DSI *dsi = (DSI *) obj->handle;
+ int rc_idx;
u_int32_t err, cmd;
u_int8_t function;
struct sigaction action;
obj->exit = afp_dsi_die;
obj->reply = (int (*)()) dsi_cmdreply;
obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
-
obj->sleep = afp_dsi_sleep;
- child.obj = obj;
- child.tickle = child.flags = 0;
+ dsi->tickle = 0;
memset(&action, 0, sizeof(action));
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( 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, SIGHUP);
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( SIGTERM, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
-#ifdef SERVERTEXT
/* Added for server message support */
action.sa_handler = afp_dsi_getmesg;
sigemptyset( &action.sa_mask );
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
-#endif /* SERVERTEXT */
/* SIGUSR1 - set down in 5 minutes */
action.sa_handler = afp_dsi_timedown;
sigaddset(&action.sa_mask, SIGHUP);
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
sigaddset(&action.sa_mask, SIGUSR2);
-#endif
action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
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;
+ action.sa_flags = SA_RESTART; /* dont restart, otherwi */
if ((sigaction(SIGALRM, &action, NULL) < 0) ||
(setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
afp_dsi_die(EXITERR_SYS);
afp_dsi_die(EXITERR_SYS);
/* get stuck here until the end */
- while ((cmd = dsi_receive(dsi))) {
- child.tickle = 0;
- child.flags &= ~CHILD_SLEEPING;
+ while (1) {
+ cmd = dsi_receive(dsi);
+ if (cmd == 0) {
+ /* Some error on the client connection, enter disconnected state */
+ dsi->flags |= DSI_DISCONNECTED;
+ pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+ continue; /* continue receiving until disconnect timer expires
+ * or a primary reconnect succeeds */
+ }
+
+ dsi->tickle = 0;
dsi_sleep(dsi, 0); /* wake up */
if (reload_request) {
reload_request = 0;
- load_volumes(child.obj);
+ load_volumes(AFPobj);
dircache_dump();
log_dircache_stat();
}
if (cmd == DSIFUNC_TICKLE) {
/* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
- if ((child.flags & CHILD_DIE))
+ if ((dsi->flags & DSI_DIE))
dsi_tickle(dsi);
pending_request(dsi);
continue;
}
- child.flags |= CHILD_DATA;
+ dsi->flags |= DSI_DATA;
switch(cmd) {
case DSIFUNC_CLOSE:
afp_dsi_close(obj);
- LOG(log_info, logtype_afpd, "done");
+ LOG(log_note, logtype_afpd, "done");
return;
break;
function = (u_char) dsi->commands[0];
- /* send off an afp command. in a couple cases, we take advantage
- * of the fact that we're a stream-based protocol. */
- if (afp_switch[function]) {
- dsi->datalen = DSI_DATASIZ;
- child.flags |= CHILD_RUNNING;
+ /* AFP replay cache */
+ rc_idx = REPLAYCACHE_SIZE % dsi->clientID;
+ LOG(log_debug, logtype_afpd, "DSI request ID: %u", dsi->clientID);
- LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+ if (replaycache[rc_idx].DSIreqID == dsi->clientID
+ && replaycache[rc_idx].AFPcommand == function) {
+ LOG(log_debug, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
+ dsi->clientID, AfpNum2name(function));
+ err = replaycache[rc_idx].result;
+ /* AFP replay cache end */
+ } else {
+ /* send off an afp command. in a couple cases, we take advantage
+ * of the fact that we're a stream-based protocol. */
+ if (afp_switch[function]) {
+ dsi->datalen = DSI_DATASIZ;
+ dsi->flags |= DSI_RUNNING;
- err = (*afp_switch[function])(obj,
- (char *)&dsi->commands, dsi->cmdlen,
- (char *)&dsi->data, &dsi->datalen);
+ LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
- LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
- AfpNum2name(function), AfpErr2name(err));
+ err = (*afp_switch[function])(obj,
+ (char *)&dsi->commands, dsi->cmdlen,
+ (char *)&dsi->data, &dsi->datalen);
- dir_free_invalid_q();
+ LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
+ AfpNum2name(function), AfpErr2name(err));
+
+ dir_free_invalid_q();
#ifdef FORCE_UIDGID
- /* bring everything back to old euid, egid */
- if (obj->force_uid)
- restore_uidgid ( &obj->uidgid );
+ /* bring everything back to old euid, egid */
+ if (obj->force_uid)
+ restore_uidgid ( &obj->uidgid );
#endif /* FORCE_UIDGID */
- child.flags &= ~CHILD_RUNNING;
- } else {
- LOG(log_error, logtype_afpd, "bad function %X", function);
- dsi->datalen = 0;
- err = AFPERR_NOOP;
+ dsi->flags &= ~DSI_RUNNING;
+
+ /* Add result to the AFP replay cache */
+ replaycache[rc_idx].DSIreqID = dsi->clientID;
+ replaycache[rc_idx].AFPcommand = function;
+ replaycache[rc_idx].result = err;
+ } else {
+ LOG(log_error, logtype_afpd, "bad function %X", function);
+ dsi->datalen = 0;
+ err = AFPERR_NOOP;
+ }
}
/* single shot toggle that gets set by dsi_readinit. */
- if (dsi->noreply) {
- dsi->noreply = 0;
+ if (dsi->flags & DSI_NOREPLY) {
+ dsi->flags &= ~DSI_NOREPLY;
break;
}
if (!dsi_cmdreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
- afp_dsi_die(EXITERR_CLNT);
+ dsi->flags |= DSI_DISCONNECTED;
}
break;
function = (u_char) dsi->commands[0];
if ( afp_switch[ function ] != NULL ) {
dsi->datalen = DSI_DATASIZ;
- child.flags |= CHILD_RUNNING;
+ dsi->flags |= DSI_RUNNING;
LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
AfpNum2name(function), AfpErr2name(err));
- child.flags &= ~CHILD_RUNNING;
+ dsi->flags &= ~DSI_RUNNING;
#ifdef FORCE_UIDGID
/* bring everything back to old euid, egid */
if (obj->force_uid)
if (!dsi_wrtreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
- afp_dsi_die(EXITERR_CLNT);
+ dsi->flags |= DSI_DISCONNECTED;
}
break;
options->transports = AFPTRANS_TCP; /* TCP only */
options->passwdfile = _PATH_AFPDPWFILE;
options->tickleval = 30;
- options->timeout = 4;
+ options->timeout = 4; /* 4 tickles = 2 minutes */
options->sleep = 10* 120; /* 10 h in 30 seconds tick */
+ options->disconnected = 20; /* 20 * 30 s (default tickleval) = 10 minutes */
options->server_notif = 1;
options->authprintdir = NULL;
options->signatureopt = "auto";
puts( "afpd has been compiled with support for these features:\n" );
- printf( " AFP3.x support:\t" );
-#ifdef AFP3x
- puts( "Yes" );
-#else
- puts( "No" );
-#endif
-
+ printf( " AFP3.x support:\tYes\n" );
printf( " TCP/IP Support:\t" );
puts( "Yes" );
{ "AFPVersion 2.1", 21 },
#endif /* ! NO_DDP */
{ "AFP2.2", 22 },
-#ifdef AFP3x
{ "AFPX03", 30 },
{ "AFP3.1", 31 },
- { "AFP3.2", 32 }
-#endif /* AFP3x */
+ { "AFP3.2", 32 },
+ { "AFP3.3", 33 }
};
static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
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;
}
/* ---------------------- */
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 char passwdbits, passwdminlen, loginmaxfail;
u_int32_t server_quantum;
char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
charset_t maccharset, unixcharset;
mode_t umask;
mode_t save_mask;
- int sleep;
#ifdef ADMIN_GRP
gid_t admingid;
#endif /* ADMIN_GRP */
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 <sys/uio.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/poll.h>
#include <errno.h>
+#include <sys/wait.h>
#include <atalk/logger.h>
#include <atalk/adouble.h>
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));
-#ifdef AFP3x
/* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
even if the file is open with O_LARGEFILE ! */
#ifdef SIGXFSZ
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_handler = child_handler;
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);
#endif
/* 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;
}
#include "auth.h"
#include "uam_auth.h"
-#ifdef AFP3x
#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
#ifdef TRU64
#include <netdb.h>
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;
}
if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
return -1;
+ if (dbif_txn_commit(dbd) != 1) {
+ LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn");
+ return -1;
+ }
return 0;
}
DBT key, data;
int ret;
+ LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info");
+
+ *version = -1;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = ROOTINFO_KEY;
ret = 1;
break;
case 0: /* not found */
+ LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found");
ret = 0;
break;
default:
+ LOG(log_error, logtype_cnid, "dbif_getversion: database error");
ret = -1;
break;
}
/*!
* Set CNID database version number
*
- * Initializes rootinfo key as neccessary, as does dbif_getversion
+ * Initializes rootinfo key as neccessary
* @returns -1 on error, 0 on success
*/
static int dbif_setversion(DBD *dbd, uint32_t version)
}
/*!
- * Upgrade CNID database versions
+ * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion()
*
* For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open
*/
+#define UNINTIALIZED_DB UINT32_MAX
static int dbif_upgrade(DBD *dbd)
{
- uint32_t version;
+ uint32_t version = CNID_VERSION_UNINTIALIZED_DB;
if (dbif_getversion(dbd, &version) == -1)
return -1;
+ if (version == CNID_VERSION_UNINTIALIZED_DB) {
+ version = CNID_VERSION;
+ if (dbif_setversion(dbd, CNID_VERSION) != 0)
+ return -1;
+ }
/*
* Do upgrade stuff ...
if (reindex)
LOG(log_info, logtype_cnid, "Reindexing name index...");
+ /*
+ * Upgrading from version 0 to 1 requires adding the name index below which
+ * must be done by specifying the DB_CREATE flag
+ */
uint32_t version = CNID_VERSION;
if (dbd->db_envhome && !reindex) {
if (dbif_getversion(dbd, &version) == -1)
exit(1);
LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
- if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+ if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
exit(2);
if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
#define AFP_SETACL 74
#define AFP_ACCESS 75
+/* more defines */
+#define REPLAYCACHE_SIZE 128
+
#endif
* 0: up to Netatalk 2.1.x
* 1: starting with 2.2, additional name index, used in cnid_find
*/
-#define CNID_VERSION_0 0
-#define CNID_VERSION_1 1
+#define CNID_VERSION_0 0
+#define CNID_VERSION_1 1
+#define CNID_VERSION_UNINTIALIZED_DB UINT32_MAX
/* Current CNID version */
#define CNID_VERSION CNID_VERSION_1
dsi_proto protocol;
struct dsi_block header;
struct sockaddr_storage server, client;
-
struct itimerval timer;
-
- int in_write; /* in the middle of writing multiple packets, signal handlers
- * can't write to the socket
- */
+ int tickle; /* tickle count */
+ int in_write; /* in the middle of writing multiple packets,
+ signal handlers can't write to the socket */
int msg_request; /* pending message to the client */
int down_request; /* pending SIGUSR1 down in 5 mn */
size_t statuslen;
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;
+ uint32_t flags; /* DSI flags like DSI_SLEEPING, DSI_DISCONNECTED */
const char *program;
int socket, serversock;
/* DSI session options */
#define DSIOPT_SERVQUANT 0x00 /* server request quantum */
#define DSIOPT_ATTNQUANT 0x01 /* attention quantum */
+#define DSIOPT_REPLCSIZE 0x02 /* AFP replaycache size supported by the server (that's us) */
/* DSI Commands */
#define DSIFUNC_CLOSE 1 /* DSICloseSession */
/* default port number */
#define DSI_AFPOVERTCP_PORT 548
+/* DSI session State flags */
+#define DSI_DATA (1 << 0) /* we have received a DSI command */
+#define DSI_RUNNING (1 << 1) /* we have received a AFP command */
+#define DSI_SLEEPING (1 << 2) /* we're sleeping after FPZzz */
+#define DSI_DISCONNECTED (1 << 3) /* we're in diconnected state after a socket error */
+#define DSI_DIE (1 << 4) /* SIGUSR1, going down in 5 minutes */
+#define DSI_NOREPLY (1 << 5) /* in dsi_write we generate our own replies */
+
/* basic initialization: dsi_init.c */
extern DSI *dsi_init (const dsi_proto /*protocol*/,
const char * /*program*/,
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 */
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
-
+#include <poll.h>
#ifndef NO_DDP
#include <netatalk/at.h>
#endif
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
*****************************************************************/
********************************************************/
extern int getuuidfromname( const char *name, uuidtype_t type, unsigned char *uuid);
-extern int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+extern int getnamefromuuid( const unsigned char *uuid, char **name, uuidtype_t *type);
+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);
#define VOLPBIT_XBTOTAL 10
#define VOLPBIT_BSIZE 11 /* block size */
-#ifdef AFP3x
#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? 1 : 0)
#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
* Public helper function
********************************************************/
+static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
+ 0xab, 0xcd, 0xef,
+ 0xab, 0xcd, 0xef,
+ 0xab, 0xcd, 0xef};
+
+static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
+ 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa};
+
+void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id)
+{
+ uint32_t tmp;
+
+ switch (type) {
+ case UUID_GROUP:
+ memcpy(buf, local_group_uuid, 12);
+ break;
+ case UUID_USER:
+ default:
+ memcpy(buf, local_user_uuid, 12);
+ break;
+ }
+
+ tmp = htonl(id);
+ memcpy(buf + 12, &tmp, 4);
+
+ return;
+}
+
/*
* convert ascii string that can include dashes to binary uuid.
* caller must provide a buffer.
* Interface
********************************************************/
-static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
- 0xab, 0xcd, 0xef,
- 0xab, 0xcd, 0xef,
- 0xab, 0xcd, 0xef};
-
-static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
- 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa};
-
/*
* name: give me his name
* type: and type (UUID_USER or UUID_GROUP)
if (ret != 0) {
/* Build a local UUID */
if (type == UUID_USER) {
- memcpy(uuid, local_user_uuid, 12);
struct passwd *pwd;
if ((pwd = getpwnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
name, uuidtype[type]);
goto cleanup;
}
- uint32_t id = pwd->pw_uid;
- id = htonl(id);
- memcpy(uuid + 12, &id, 4);
+ localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
} else {
- memcpy(uuid, &local_group_uuid, 12);
struct group *grp;
if ((grp = getgrnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
name, uuidtype[type]);
goto cleanup;
}
- uint32_t id = grp->gr_gid;
- id = htonl(id);
- memcpy(uuid + 12, &id, 4);
+ 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));
*/
int ad_setname(struct adouble *ad, const char *path)
{
+ int len;
+ if ((len = strlen(path)) > ADEDLEN_NAME)
+ len = ADEDLEN_NAME;
if (path && ad_getentryoff(ad, ADEID_NAME)) {
- ad_setentrylen( ad, ADEID_NAME, strlen( path ));
- memcpy(ad_entry( ad, ADEID_NAME ), path, ad_getentrylen( ad, ADEID_NAME ));
+ ad_setentrylen( ad, ADEID_NAME, len);
+ memcpy(ad_entry( ad, ADEID_NAME ), path, len);
return 1;
}
return 0;
&(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;
}
u_int32_t len, nlen;
u_int16_t id;
- if (dsi->asleep)
+ if (dsi->flags & DSI_SLEEPING)
return 1;
if (dsi->in_write) {
void dsi_close(DSI *dsi)
{
/* server generated. need to set all the fields. */
- if (!dsi->asleep) {
+ if (!(dsi->flags & DSI_SLEEPING)) {
dsi->header.dsi_flags = DSIFL_REQUEST;
dsi->header.dsi_command = DSIFUNC_CLOSE;
dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
#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 */
void dsi_opensession(DSI *dsi)
{
u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+ int offs;
/* parse options */
while (i < dsi->cmdlen) {
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = 0;
/* dsi->header.dsi_command = DSIFUNC_OPEN;*/
- dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+
+ dsi->cmdlen = 2 * (2 + sizeof(i)); /* length of data. dsi_send uses it. */
+
+ /* DSI Option Server Request Quantum */
dsi->commands[0] = DSIOPT_SERVQUANT;
dsi->commands[1] = sizeof(i);
i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN ||
DSI_SERVQUANT_DEF : dsi->server_quantum);
memcpy(dsi->commands + 2, &i, sizeof(i));
+ /* AFP replaycache size option */
+ offs = 2 + sizeof(i);
+ dsi->commands[offs] = DSIOPT_REPLCSIZE;
+ dsi->commands[offs+1] = sizeof(i);
+ i = htonl(REPLAYCACHE_SIZE);
+ memcpy(dsi->commands + offs + 2, &i, sizeof(i));
dsi_send(dsi);
}
const size_t size, const int err)
{
- dsi->noreply = 1; /* we will handle our own replies */
+ dsi->flags |= DSI_NOREPLY; /* we will handle our own replies */
dsi->header.dsi_flags = DSIFL_REPLY;
/*dsi->header.dsi_command = DSIFUNC_CMD;*/
dsi->header.dsi_len = htonl(size);
else { /* eof or error */
/* don't log EOF error if it's just after connect (OSX 10.3 probe) */
if (len || stored || dsi->read_count) {
- LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
+ if (! (dsi->flags & DSI_DISCONNECTED)
+)
+ LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
}
break;
}
*/
void dsi_sleep(DSI *dsi, const int state)
{
- dsi->asleep = state;
+ if (state)
+ dsi->flags |= DSI_SLEEPING;
+ else
+ dsi->flags &= ~DSI_SLEEPING;
}
/* ---------------------------------------
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();
{
char block[DSI_BLOCKSIZ];
u_int16_t id;
- int ret;
- if (dsi->asleep || dsi->in_write)
+ if ((dsi->flags & DSI_SLEEPING) || dsi->in_write)
return 1;
id = htons(dsi_serverID(dsi));
memcpy(block + 2, &id, sizeof(id));
/* code = len = reserved = 0 */
- ret = dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
- /* we don't really care if we can't send a tickle, it will fail
- * elsewhere
- */
- ret = (ret == -1 || ret == DSI_BLOCKSIZ);
- return ret;
+ return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
}
/*
- * $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 <string.h>
#include <unistd.h>
#include <signal.h>
+#include <errno.h>
#include <sys/types.h>
#include <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 */
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif /* ! WIFEXITED */
+#ifndef WIFSTOPPED
+#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(status) ((status) & 0x7f)
+#endif
+
/* 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;
+
+ /* if we already have an entry. just return. */
+ if (child = resolve_child(fork->table, pid))
+ goto exit;
+
+ 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];
- child->pid = pid;
- child->valid = 0;
- child->killed = 0;
- hash_child(fork->table, child);
- children->count++;
- pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+ hash_child(fork->table, child);
+ children->count++;
- return 0;
+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);
-
+
}
/*
* All rights reserved. See COPYRIGHT.
+ *
+ * IPC over socketpair between parent and children.
*/
#ifdef HAVE_CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ipc.h>
-#include <sys/shm.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];
-}
-
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
- return pipe_fd[0];
-}
+ 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"};
-/* ----------------- */
-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;
+}