netatalk  4.6.0
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
server_ipc.c File Reference
#include <errno.h>
#include <limits.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <atalk/dsi.h>
#include <atalk/errchk.h>
#include <atalk/globals.h>
#include <atalk/logger.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
#include <atalk/util.h>

Data Structures

struct  ipc_header
struct  hint_entry
struct  [struct].hint_buf
struct  [struct].rate_track
struct  [struct].reset_seen

Macros

#define HINT_RATE_LIMIT   1000
#define HINT_RESET_RATE_LIMIT   8
#define RESET_SEEN_SIZE   8
#define HINT_RESET_WAIT_BUDGET   4
#define HINT_RESET_WAIT_MS   20
#define RATE_TRACK_SIZE   256

Typedefs

typedef struct ipc_header ipc_header_t

Functions

static int rate_slot (pid_t child_pid)
static int check_and_increment_rate (pid_t child_pid)
static int check_and_increment_reset_rate (pid_t child_pid)
static int reset_already_broadcast (uint32_t tag)
 Whether this volume's reset has just been broadcast.
static void reset_broadcast_forget (uint32_t tag)
 Make a volume's reset broadcastable again.
static int hint_write_batch (int fd, const char *buf, int len, int has_reset, int *waits_left)
 Write a sibling's hint batch, waiting once if it carries a reset.
static int serialize_hint (char *buf, const struct hint_entry *e)
 Serialize a hint_entry to IPC wire format.
static int ipc_kill_token (struct ipc_header *ipc, server_child_t *children)
 Pass afp_socket to old disconnected session if one has a matching token.
static int ipc_get_session (struct ipc_header *ipc, server_child_t *children)
static int ipc_login_done (const struct ipc_header *ipc, server_child_t *children)
static int ipc_set_session_token (const struct ipc_header *ipc, server_child_t *children)
static int ipc_set_state (struct ipc_header *ipc, server_child_t *children)
static int ipc_set_volumes (struct ipc_header *ipc, server_child_t *children)
static int ipc_relay_cache_hint (struct ipc_header *ipc, server_child_t *children)
 Buffer a dircache hint for batched relay to siblings.
int ipc_server_read (server_child_t *children, int fd)
 Read a IPC message from a child.
int ipc_child_write (AFPObj *obj, uint16_t command, size_t len, void *msg)
int ipc_child_state (AFPObj *obj, uint16_t state)
int hint_buf_count (void)
 Return current number of buffered hints.
void hint_flush_pending (server_child_t *children)
 Flush all buffered hints to sibling children.
unsigned long long ipc_get_hints_sent (void)
unsigned long long ipc_get_hints_dropped (void)
static const char * cache_hint_name (uint8_t event)
int ipc_send_cache_hint (const AFPObj *obj, uint16_t vid, cnid_t cnid, uint8_t event)
 Send a dircache invalidation hint from child to parent.

Variables

static char * ipc_cmd_str []
static struct  hint_buf
static struct  rate_track [RATE_TRACK_SIZE]
static unsigned long long hints_batched = 0
static unsigned long long hints_rate_dropped = 0
static unsigned long long flush_count = 0
static struct  reset_seen [RESET_SEEN_SIZE]
static unsigned long long hints_sent = 0
static unsigned long long hints_dropped = 0

Detailed Description

IPC over socketpair between parent and children.

Macro Definition Documentation

◆ HINT_RATE_LIMIT

#define HINT_RATE_LIMIT   1000

◆ HINT_RESET_RATE_LIMIT

#define HINT_RESET_RATE_LIMIT   8

◆ HINT_RESET_WAIT_BUDGET

#define HINT_RESET_WAIT_BUDGET   4

◆ HINT_RESET_WAIT_MS

#define HINT_RESET_WAIT_MS   20

◆ RATE_TRACK_SIZE

#define RATE_TRACK_SIZE   256

◆ RESET_SEEN_SIZE

#define RESET_SEEN_SIZE   8

Typedef Documentation

◆ ipc_header_t

typedef struct ipc_header ipc_header_t

Function Documentation

◆ cache_hint_name()

const char * cache_hint_name ( uint8_t event)
static

◆ check_and_increment_rate()

int check_and_increment_rate ( pid_t child_pid)
static

◆ check_and_increment_reset_rate()

int check_and_increment_reset_rate ( pid_t child_pid)
static

◆ hint_buf_count()

int hint_buf_count ( void )

Return current number of buffered hints.

Used by main event loop to decide poll timeout:

  • count > 0: use HINT_FLUSH_INTERVAL_MS timeout
  • count == 0: use -1 (infinite, block until event)

◆ hint_flush_pending()

void hint_flush_pending ( server_child_t * children)

Flush all buffered hints to sibling children.

Called from the parent main event loop when:

  1. The 50ms poll timeout expires and hint_buf.count > 0
  2. hint_buf.count reaches HINT_BUF_SIZE after ipc_server_read

Iterates the child table directly:

  • Signals are blocked (no SIGCHLD can modify table)
  • SIGCHLD already processed before flush (dead children removed)
  • No other thread modifies the table

Performs priority sorting, PIPE_BUF-safe chunked writes.

While this function writes to child pipes, new IPC messages from children accumulate in the kernel socket buffer and are read on the next poll() iteration.

◆ hint_write_batch()

int hint_write_batch ( int fd,
const char * buf,
int len,
int has_reset,
int * waits_left )
static

Write a sibling's hint batch, waiting once if it carries a reset.

A full sibling pipe drops a best-effort batch: those hints only cost a lookup. A batch carrying a reset waits briefly for pipe space instead, since the sibling would otherwise keep resolving recycled CNIDs. Waits are budgeted per second across flushes so backed-up siblings cannot stall the master.

Parameters
[in]fdsibling's hint pipe
[in]bufserialized batch
[in]lenbytes to write
[in]has_resetbatch contains a CACHE_HINT_VOLUME_RESET
[in,out]waits_leftthis second's remaining waits, spent here
Returns
0 on success, -1 if the batch was not delivered

◆ ipc_child_state()

int ipc_child_state ( AFPObj * obj,
uint16_t state )

◆ ipc_child_write()

int ipc_child_write ( AFPObj * obj,
uint16_t command,
size_t len,
void * msg )

◆ ipc_get_hints_dropped()

unsigned long long ipc_get_hints_dropped ( void )

◆ ipc_get_hints_sent()

unsigned long long ipc_get_hints_sent ( void )

◆ ipc_get_session()

int ipc_get_session ( struct ipc_header * ipc,
server_child_t * children )
static

◆ ipc_kill_token()

int ipc_kill_token ( struct ipc_header * ipc,
server_child_t * children )
static

Pass afp_socket to old disconnected session if one has a matching token.

Returns
-1 on error, 0 if no matching session was found, 1 if session was found and socket passed

◆ ipc_login_done()

int ipc_login_done ( const struct ipc_header * ipc,
server_child_t * children )
static

◆ ipc_relay_cache_hint()

int ipc_relay_cache_hint ( struct ipc_header * ipc,
server_child_t * children )
static

Buffer a dircache hint for batched relay to siblings.

Appends to hint_buf array. Single-threaded — no lock needed. If buffer is full, the hint is dropped (caller should flush first).

◆ ipc_send_cache_hint()

int ipc_send_cache_hint ( const AFPObj * obj,
uint16_t vid,
cnid_t cnid,
uint8_t event )

Send a dircache invalidation hint from child to parent.

Called directly from AFP command handlers that modify dircache state. Independent of the external FCE system — always active when IPC is available.

Uses a direct non-blocking write() to the IPC socketpair instead of ipc_child_write()/writet() — this ensures the AFP command handler is never blocked waiting for IPC buffer space. If the kernel socket buffer is full (parent not draining fast enough), the hint is silently dropped. Hints are best-effort optimizations, and the dircache validation mechanism & graceful fail-on-use detection makes drops safe.

CACHE_HINT_VOLUME_RESET is the exception: a sibling that misses it keeps resolving CNIDs that the wipe has recycled onto other files, so it waits briefly for pipe space and reports failure to the caller.

The 22-byte message (14-byte IPC header + 8-byte payload) is well under the kernel socket buffer size, so partial writes cannot occur when space is available.

Parameters
[in]objAFPObj with ipc_fd
[in]vidVolume ID (network byte order, matches vol->v_vid)
[in]cnidCNID of affected file/dir (network byte order)
[in]eventHint type: one of the CACHE_HINT_* values
Returns
0 on success (or graceful drop), -1 on fatal error or an undeliverable CACHE_HINT_VOLUME_RESET

◆ ipc_server_read()

int ipc_server_read ( server_child_t * children,
int fd )

Read a IPC message from a child.

This is using an fd with non-blocking IO, so EAGAIN is not an error

Parameters
[in,out]childrenpointer to our structure with all childs
[in]fdIPC socket with child
Returns
-1 on error, 0 on success

◆ ipc_set_session_token()

int ipc_set_session_token ( const struct ipc_header * ipc,
server_child_t * children )
static

◆ ipc_set_state()

int ipc_set_state ( struct ipc_header * ipc,
server_child_t * children )
static

◆ ipc_set_volumes()

int ipc_set_volumes ( struct ipc_header * ipc,
server_child_t * children )
static

◆ rate_slot()

int rate_slot ( pid_t child_pid)
static

◆ reset_already_broadcast()

int reset_already_broadcast ( uint32_t tag)
static

Whether this volume's reset has just been broadcast.

One notification per volume is all a sibling needs, and each one costs a forced flush to every session, so a repeat inside the same second is dropped. Records the tag when it is new.

Parameters
[in]tagcnid_volume_tag() of the reset volume, network byte order

◆ reset_broadcast_forget()

void reset_broadcast_forget ( uint32_t tag)
static

Make a volume's reset broadcastable again.

A sibling that missed a reset batch has no other redelivery path, so a delivery failure must not leave the tag deduped against the next duplicate.

Parameters
[in]tagcnid_volume_tag() of the reset volume, network byte order

◆ serialize_hint()

int serialize_hint ( char * buf,
const struct hint_entry * e )
static

Serialize a hint_entry to IPC wire format.

Writes IPC_HEADERLEN + sizeof(ipc_cache_hint_payload) = 22 bytes to the output buffer. The header PID/UID fields are set to the source child's PID (for receiver logging) with UID 0.

Parameters
[out]bufOutput buffer (must have ≥ 22 bytes available)
[in]eHint entry to serialize
Returns
Number of bytes written (always 22)

Variable Documentation

◆ flush_count

unsigned long long flush_count = 0
static

◆ hint_buf

struct [struct].hint_buf hint_buf

◆ hints_batched

unsigned long long hints_batched = 0
static

◆ hints_dropped

unsigned long long hints_dropped = 0
static

◆ hints_rate_dropped

unsigned long long hints_rate_dropped = 0
static

◆ hints_sent

unsigned long long hints_sent = 0
static

◆ ipc_cmd_str

char* ipc_cmd_str[]
static
Initial value:
= { "IPC_DISCOLDSESSION",
"IPC_GETSESSION",
"IPC_STATE",
"IPC_VOLUMES",
"IPC_LOGINDONE",
"IPC_CACHE_HINT",
"IPC_SESSIONTOKEN"
}

◆ rate_track

◆ reset_seen