netatalk  4.5.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 <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

Macros

#define HINT_RATE_LIMIT   1000 /* Max hints/second per child (attack guard) */
#define RATE_TRACK_SIZE   256

Typedefs

typedef struct ipc_header ipc_header_t

Functions

static int check_and_increment_rate (pid_t child_pid)
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)
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 []
struct { 
   struct hint_entry   entries [HINT_BUF_SIZE
   int   count 
hint_buf
struct { 
   pid_t   pid 
   time_t   window_start 
   int   count_in_window 
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 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 /* Max hints/second per child (attack guard) */

◆ RATE_TRACK_SIZE

#define RATE_TRACK_SIZE   256

Typedef Documentation

◆ ipc_header_t

typedef struct ipc_header ipc_header_t

Function Documentation

◆ check_and_increment_rate()

int check_and_increment_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.

◆ 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.

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: CACHE_HINT_REFRESH, CACHE_HINT_DELETE, or CACHE_HINT_DELETE_CHILDREN
Returns
0 on success (or graceful drop), -1 on fatal error

◆ 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

◆ 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

◆ count

int count

◆ count_in_window

int count_in_window

◆ entries

struct hint_entry entries[HINT_BUF_SIZE]

◆ flush_count

unsigned long long flush_count = 0
static

◆ [struct]

struct { ... } 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"
}

◆ pid

pid_t pid

◆ [struct]

struct { ... } rate_track[RATE_TRACK_SIZE]

◆ window_start

time_t window_start