netatalk: server_ipc.c File Reference

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] | obj | AFPObj with ipc_fd
  [in] | vid | Volume ID (network byte order, matches vol->v_vid)
  [in] | cnid | CNID of affected file/dir (network byte order)
  [in] | event | Hint 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] | children | pointer to our structure with all childs
  [in] | fd | IPC 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] | buf | Output buffer (must have ≥ 22 bytes available)
  [in] | e | Hint 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