ZSYS(3)

✨ zsys(3) β€” Class for system-level methods ✨

okay bestie, you've landed on the CZMQ Manual, section 3, and this is the zsys page β€” the class for system-level methods. 🫢

Real talk: zsys is the backstage crew of CZMQ. It handles the boring-but-iconic stuff β€” initializing the layer, catching interrupts, poking at files, threads, UDP beacons, hostnames, all of it. You don't always call it yourself, but it's always there for you. πŸ’…

heads up, bestie: the first chunk of this class is stable (basically frozen, only touched in emergencies) and there's also a pile of draft methods in here that can absolutely change on you. Drafts aren't in stable releases by default β€” you gotta build with --enable-drafts to get them. So like, vibe check before you ship. 😭

🌸 SYNOPSIS 🌸

here's the whole cast, in order, no skips. ✨

//  This is a stable class, and may not change except for emergencies. It
//  is provided in stable builds.
//  This class has draft methods, which may change over time. They are not
//  in stable releases, by default. Use --enable-drafts to enable.

first up, the callback type for the interrupt signal handler β€” this is the shape your handler function has to have:

// Callback for interrupt signal handler
typedef void (zsys_handler_fn) (
    int signal_value);

πŸŽ€ getting the party started

zsys_init sets up the CZMQ zsys layer. it also happens automatically the second you make a socket or an actor, but calling it yourself means you can force it earlier β€” so for example your logging is properly set up before you start doing anything. not threadsafe, so only call it from the main thread. totally safe to call more than once, though. it hands you back the global CZMQ context. πŸ‘‘

//  Initialize CZMQ zsys layer; this happens automatically when you create
//  a socket or an actor; however this call lets you force initialization
//  earlier, so e.g. logging is properly set-up before you start working.
//  Not threadsafe, so call only from main thread. Safe to call multiple
//  times. Returns global CZMQ context.
CZMQ_EXPORT void *
    zsys_init (void);

zsys_shutdown is the optional goodbye. normally this happens on its own when the process exits, but you can force it earlier β€” which is lowkey useful because it dodges any potential problems with atexit() ordering, especially with Windows dlls. πŸ¦‹

//  Optionally shut down the CZMQ zsys layer; this normally happens automatically
//  when the process exits; however this call lets you force a shutdown
//  earlier, avoiding any potential problems with atexit() ordering, especially
//  with Windows dlls.
CZMQ_EXPORT void
    zsys_shutdown (void);

πŸ”₯ sockets, but make it internal

zsys_socket gets you a new ZMQ socket and automagically creates a ZMQ context if this is the first time. the caller is responsible for destroying the ZMQ socket before the process exits, otherwise you get a ZMQ deadlock. also β€” do not use this in CZMQ apps, use zsock_new() instead. πŸ™ˆ

⚠️ *** This is for CZMQ internal use only and may change arbitrarily *** β€” that's not me being dramatic, that's literally what the page says.
//  Get a new ZMQ socket, automagically creating a ZMQ context if this is
//  the first time. Caller is responsible for destroying the ZMQ socket
//  before process exits, to avoid a ZMQ deadlock. Note: you should not use
//  this method in CZMQ apps, use zsock_new() instead.
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT void *
    zsys_socket (int type, const char *filename, size_t line_nbr);

zsys_close destroys/closes a ZMQ socket. you should call it for every socket you made with zsys_socket(). yes, every single one. 😀

//  Destroy/close a ZMQ socket. You should call this for every socket you
//  create using zsys_socket().
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT int
    zsys_close (void *handle, const char *filename, size_t line_nbr);

zsys_sockname returns the ZMQ socket name for a socket type. 🏷️

//  Return ZMQ socket name for socket type
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT char *
    zsys_sockname (int socktype);

zsys_create_pipe makes a pipe β€” two PAIR sockets connected over inproc. the pipe is configured to use the zsys_pipehwm setting. returns the frontend socket on success, NULL if it failed. 🫧

//  Create a pipe, which consists of two PAIR sockets connected over inproc.
//  The pipe is configured to use the zsys_pipehwm setting. Returns the
//  frontend socket successful, NULL if failed.
CZMQ_EXPORT zsock_t *
    zsys_create_pipe (zsock_t **backend_p);

πŸ’– interrupt handlers

zsys_handler_set sets the interrupt handler β€” and it saves the default handlers so that a zsys_handler_reset () can restore them later. call it multiple times and the last handler wins. if handler_fn is NULL, it disables the default SIGINT/SIGTERM handling in CZMQ. πŸ’€

//  Set interrupt handler; this saves the default handlers so that a
//  zsys_handler_reset () can restore them. If you call this multiple times
//  then the last handler will take affect. If handler_fn is NULL, disables
//  default SIGINT/SIGTERM handling in CZMQ.
CZMQ_EXPORT void
    zsys_handler_set (zsys_handler_fn *handler_fn);

zsys_handler_reset resets the interrupt handler β€” call it at exit if you need to. 🧚

//  Reset interrupt handler, call this at exit if needed
CZMQ_EXPORT void
    zsys_handler_reset (void);

zsys_catch_interrupts sets the default interrupt handler, so Ctrl-C or SIGTERM will set zsys_interrupted. it's idempotent, so calling it multiple times is safe. you can suppress it with ZSYS_SIGHANDLER=false. πŸ›‘

//  Set default interrupt handler, so Ctrl-C or SIGTERM will set
//  zsys_interrupted. Idempotent; safe to call multiple times.
//  Can be suppressed by ZSYS_SIGHANDLER=false
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT void
    zsys_catch_interrupts (void);

πŸ“ file things (the glamorous part)

zsys_file_exists returns 1 if the file exists, otherwise zero. simple. iconic. ✨

//  Return 1 if file exists, else zero
CZMQ_EXPORT bool
    zsys_file_exists (const char *filename);

zsys_file_modified gives you the file modification time, and returns 0 if the file doesn't exist. πŸ•°οΈ

//  Return file modification time. Returns 0 if the file does not exist.
CZMQ_EXPORT time_t
    zsys_file_modified (const char *filename);

zsys_file_mode returns the file mode. it provides at least support for the POSIX S_ISREG(m) and S_ISDIR(m) macros plus the S_IRUSR and S_IWUSR bits, on all boxes. returns a mode_t cast to int, or -1 on error. πŸ‘€

//  Return file mode; provides at least support for the POSIX S_ISREG(m)
//  and S_ISDIR(m) macros and the S_IRUSR and S_IWUSR bits, on all boxes.
//  Returns a mode_t cast to int, or -1 in case of error.
CZMQ_EXPORT int
    zsys_file_mode (const char *filename);

zsys_file_delete deletes a file, and it does not complain if the file isn't even there. no drama, no notes. πŸ’…

//  Delete file. Does not complain if the file is absent
CZMQ_EXPORT int
    zsys_file_delete (const char *filename);

zsys_file_stable checks if a file is 'stable'. 🌈

//  Check if file is 'stable'
CZMQ_EXPORT bool
    zsys_file_stable (const char *filename);

zsys_dir_create creates a file path if it doesn't exist β€” and the file path is treated as a printf format. πŸͺ„

//  Create a file path if it doesn't exist. The file path is treated as
//  printf format.
CZMQ_EXPORT int
    zsys_dir_create (const char *pathname, ...);

zsys_dir_delete removes a file path if it's empty, and yep, the pathname is treated as printf format too. 🧹

//  Remove a file path if empty; the pathname is treated as printf format.
CZMQ_EXPORT int
    zsys_dir_delete (const char *pathname, ...);

zsys_dir_change moves to a specified working directory. returns 0 if OK, -1 if it failed. πŸšΆβ€β™€οΈ

//  Move to a specified working directory. Returns 0 if OK, -1 if this failed.
CZMQ_EXPORT int
    zsys_dir_change (const char *pathname);

zsys_file_mode_private sets the private file creation mode β€” every file created from here on is readable/writable by the owner only. πŸ”’

//  Set private file creation mode; all files created from here will be
//  readable/writable by the owner only.
CZMQ_EXPORT void
    zsys_file_mode_private (void);

zsys_file_mode_default resets the default file creation mode β€” from here on, files use the process file mode defaults. πŸ”“

//  Reset default file creation mode; all files created from here will use
//  process file mode defaults.
CZMQ_EXPORT void
    zsys_file_mode_default (void);

πŸ‘‘ version + string formatting

zsys_version returns the CZMQ version for run-time API detection β€” it writes the version number into the provided fields, as long as the reference isn't null in each case. πŸ“Œ

//  Return the CZMQ version for run-time API detection; returns version
//  number into provided fields, providing reference isn't null in each case.
CZMQ_EXPORT void
    zsys_version (int *major, int *minor, int *patch);

zsys_sprintf formats a string using printf formatting and returns a freshly allocated buffer. if there wasn't enough memory, it returns NULL. free the returned string with zstr_free(). 🎁

//  Format a string using printf formatting, returning a freshly allocated
//  buffer. If there was insufficient memory, returns NULL. Free the returned
//  string using zstr_free().
CZMQ_EXPORT char *
    zsys_sprintf (const char *format, ...);

zsys_vprintf is the same deal but takes a va_list argument β€” returns a freshly allocated buffer, returns NULL if there wasn't enough memory, and you free it with zstr_free(). 🫢

//  Format a string with a va_list argument, returning a freshly allocated
//  buffer. If there was insufficient memory, returns NULL. Free the returned
//  string using zstr_free().
CZMQ_EXPORT char *
    zsys_vprintf (const char *format, va_list argptr);

πŸ¦‹ UDP beacon era

zsys_udp_new creates a UDP beacon socket. if the routable option is true it uses multicast (not yet implemented), otherwise it uses broadcast. and no cap: this method and related ones might eventually be moved to a zudp class. 😭

//  Create UDP beacon socket; if the routable option is true, uses
//  multicast (not yet implemented), else uses broadcast. This method
//  and related ones might _eventually_ be moved to a zudp class.
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT SOCKET
    zsys_udp_new (bool routable);

zsys_udp_close closes a UDP socket. bye bestie. πŸ‘‹

//  Close a UDP socket
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT int
    zsys_udp_close (SOCKET handle);

zsys_udp_send sends a zframe to a UDP socket, and returns -1 if sending failed because the interface disappeared β€” which happens easily with WiFi, so don't take it personally. πŸ“‘

//  Send zframe to UDP socket, return -1 if sending failed due to
//  interface having disappeared (happens easily with WiFi)
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT int
    zsys_udp_send (SOCKET udpsock, zframe_t *frame, inaddr_t *address, int addrlen);

zsys_udp_recv receives a zframe from a UDP socket and sets the address of the peer that sent it. the peername must be a char [INET_ADDRSTRLEN] array if IPv6 is disabled, or NI_MAXHOST if it's enabled. returns NULL when it fails to get the peer address. 🎯

//  Receive zframe from UDP socket, and set address of peer that sent it
//  The peername must be a char [INET_ADDRSTRLEN] array if IPv6 is disabled or
//  NI_MAXHOST if it's enabled. Returns NULL when failing to get peer address.
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT zframe_t *
    zsys_udp_recv (SOCKET udpsock, char *peername, int peerlen);

zsys_socket_error handles an I/O error on some socket operation β€” it will report and die on fatal errors, and continue silently on "try again" errors. dramatic but fair. πŸ’€

//  Handle an I/O error on some socket operation; will report and die on
//  fatal errors, and continue silently on "try again" errors.
//  *** This is for CZMQ internal use only and may change arbitrarily ***
CZMQ_EXPORT void
    zsys_socket_error (const char *reason);

🌈 hostname, daemonizing, running as somebody else

zsys_hostname returns the current host name, for use in public tcp:// endpoints. the caller gets a freshly allocated string and should free it using zstr_free(). if the host name is not resolvable, it returns NULL. 🏠

//  Return current host name, for use in public tcp:// endpoints. Caller gets
//  a freshly allocated string, should free it using zstr_free(). If the host
//  name is not resolvable, returns NULL.
CZMQ_EXPORT char *
    zsys_hostname (void);

zsys_daemonize moves the current process into the background. the precise effect depends on the operating system: on POSIX boxes it moves to a specified working directory (if specified), closes all file handles, reopens stdin, stdout and stderr to the null device, and sets the process to ignore SIGHUP. on Windows, it does nothing. returns 0 if OK, -1 if there was an error. πŸŒ™

//  Move the current process into the background. The precise effect depends
//  on the operating system. On POSIX boxes, moves to a specified working
//  directory (if specified), closes all file handles, reopens stdin, stdout,
//  and stderr to the null device, and sets the process to ignore SIGHUP. On
//  Windows, does nothing. Returns 0 if OK, -1 if there was an error.
CZMQ_EXPORT int
    zsys_daemonize (const char *workdir);

zsys_run_as drops the process ID into the lockfile with an exclusive lock, and switches the process to the specified group and/or user. any of the arguments may be null, which means it's a no-op. returns 0 on success, -1 on failure.

🚨 okay listen carefully bestie: if you combine this with zsys_daemonize, run it after, not before that method β€” otherwise the lockfile will hold the wrong process ID. fr fr.
//  Drop the process ID into the lockfile, with exclusive lock, and switch
//  the process to the specified group and/or user. Any of the arguments
//  may be null, indicating a no-op. Returns 0 on success, -1 on failure.
//  Note if you combine this with zsys_daemonize, run after, not before
//  that method, or the lockfile will hold the wrong process ID.
CZMQ_EXPORT int
    zsys_run_as (const char *lockfile, const char *group, const char *user);

zsys_has_curve returns true if the underlying libzmq supports CURVE security. it uses a heuristic probe according to the version of libzmq being used. πŸ”

//  Returns true if the underlying libzmq supports CURVE security.
//  Uses a heuristic probe according to the version of libzmq being used.
CZMQ_EXPORT bool
    zsys_has_curve (void);

πŸ’œ tuning the ZMQ context (do it BEFORE any socket exists, i promise)

zsys_set_io_threads configures how many I/O threads ZeroMQ will use. a good rule of thumb is one thread per gigabit of traffic in or out. the default is 1, which is sufficient for most applications. if the environment variable ZSYS_IO_THREADS is defined, that provides the default.

⚠️ note that this method is valid only before any socket is created. periodt.
//  Configure the number of I/O threads that ZeroMQ will use. A good
//  rule of thumb is one thread per gigabit of traffic in or out. The
//  default is 1, sufficient for most applications. If the environment
//  variable ZSYS_IO_THREADS is defined, that provides the default.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_set_io_threads (size_t io_threads);

zsys_set_thread_sched_policy configures the scheduling policy of the ZMQ context thread pool. not available on Windows. see the sched_setscheduler man page or sched.h for more information. if the environment variable ZSYS_THREAD_SCHED_POLICY is defined, that provides the default. and again β€” valid only before any socket is created. πŸ—“οΈ

//  Configure the scheduling policy of the ZMQ context thread pool.
//  Not available on Windows. See the sched_setscheduler man page or sched.h
//  for more information. If the environment variable ZSYS_THREAD_SCHED_POLICY
//  is defined, that provides the default.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_set_thread_sched_policy (int policy);

zsys_set_thread_priority configures the scheduling priority of the ZMQ context thread pool. not available on Windows. see the sched_setscheduler man page or sched.h for more information. if the environment variable ZSYS_THREAD_PRIORITY is defined, that provides the default. valid only before any socket is created. βš–οΈ

//  Configure the scheduling priority of the ZMQ context thread pool.
//  Not available on Windows. See the sched_setscheduler man page or sched.h
//  for more information. If the environment variable ZSYS_THREAD_PRIORITY is
//  defined, that provides the default.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_set_thread_priority (int priority);

zsys_set_thread_name_prefix configures the numeric prefix on each thread created for the internal context's thread pool. this option is only supported on Linux. if the environment variable ZSYS_THREAD_NAME_PREFIX is defined, that provides the default. and you already know β€” valid only before any socket is created. 🏷️

//  Configure the numeric prefix to each thread created for the internal
//  context's thread pool. This option is only supported on Linux.
//  If the environment variable ZSYS_THREAD_NAME_PREFIX is defined, that
//  provides the default.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_set_thread_name_prefix (int prefix);

zsys_thread_name_prefix just returns the thread name prefix. 🫧

//  Return thread name prefix.
CZMQ_EXPORT int
    zsys_thread_name_prefix (void);

zsys_thread_affinity_cpu_add adds a specific CPU to the affinity list of the ZMQ context thread pool. only supported on Linux, and valid only before any socket is created. 🧲

//  Adds a specific CPU to the affinity list of the ZMQ context thread pool.
//  This option is only supported on Linux.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_thread_affinity_cpu_add (int cpu);

zsys_thread_affinity_cpu_remove removes a specific CPU from that affinity list. only supported on Linux, and valid only before any socket is created. 🧹

//  Removes a specific CPU to the affinity list of the ZMQ context thread pool.
//  This option is only supported on Linux.
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_thread_affinity_cpu_remove (int cpu);

and finally for this chunk: zsys_set_max_sockets configures how many sockets ZeroMQ will allow. the default is 1024. the actual limit depends on the system, and you can query it with zsys_socket_limit (). a value of zero means "maximum". and yup β€” valid only before any socket is created. πŸ”’

//  Configure the number of sockets that ZeroMQ will allow. The default
//  is 1024. The actual limit depends on the system, and you can query it
//  by using zsys_socket_limit (). A value of zero means "maximum".
//  Note that this method is valid only before any socket is created.
CZMQ_EXPORT void
    zsys_set_max_sockets (size_t max_sockets);

okay bestie that's the whole synopsis, every single declaration accounted for, nothing skipped, nothing merged, all facts intact πŸ’– and there is so much more coming β€” keep reading, we are absolutely not done here ✨

zsys_socket_limit</p> πŸ’…

CZMQ_EXPORT size_t
    zsys_socket_limit (void);

Gives you back the max number of ZeroMQ sockets your system can handle. Like, the ceiling. The hard cap. Returns a size_t because bestie, we're not doing negative sockets.


zsys_set_max_msgsz</p> ✨

CZMQ_EXPORT void
    zsys_set_max_msgsz (int max_msgsz);

Sets the maximum allowed size of a message you can send. Default is INT_MAX, so like… go off, queen, but maybe don't.

zsys_max_msgsz</p> πŸŽ€

CZMQ_EXPORT int
    zsys_max_msgsz (void);

Reads back the maximum message size. Returns an int. That's it, that's the function.


zsys_set_linger</p> πŸ¦‹

CZMQ_EXPORT void
    zsys_set_linger (size_t linger);

Sets the default linger timeout in msecs for new zsock instances. You can also set this separately on each individual zsock_t instance, iykyk.

The default linger time is zero, meaning any pending messages get dropped. Gone. Not sorry. If the environment variable ZSYS_LINGER is defined, that provides the default.

Heads up bestie: process exit will typically be delayed by the linger time. So a big linger is gonna make your program sit there like it's waiting for its iced coffee.

zsys_set_sndhwm</p> 🌸

CZMQ_EXPORT void
    zsys_set_sndhwm (size_t sndhwm);

Sets the default outgoing pipe limit (HWM) for new zsock instances. You can also set this separately on each zsock_t instance.

Default HWM is 1,000, on all versions of ZeroMQ. If the environment variable ZSYS_SNDHWM is defined, that provides the default.

⚠️ A value of zero means no limit, i.e. infinite memory consumption. It's giving "I'll deal with it later" and later is never.

zsys_set_rcvhwm</p> πŸ’–

CZMQ_EXPORT void
    zsys_set_rcvhwm (size_t rcvhwm);

Same energy as above but for the default incoming pipe limit (HWM) on new zsock instances. Also settable per-zsock_t.

Default HWM is 1,000, on all versions of ZeroMQ. If the environment variable ZSYS_RCVHWM is defined, that provides the default.

⚠️ And again: zero means no limit, i.e. infinite memory consumption. Do not.

zsys_set_pipehwm</p> 🧚

CZMQ_EXPORT void
    zsys_set_pipehwm (size_t pipehwm);

Sets the default HWM for zactor internal pipes. This one gets set on both ends of the pipe, and it applies to outgoing messages only (sndhwm).

Default HWM is 1,000, on all versions of ZeroMQ. If the environment variable ZSYS_ACTORHWM is defined, that provides the default.

⚠️ You already know what zero means here. No limit. Infinite memory consumption. The drama.

zsys_pipehwm</p> ⭐

CZMQ_EXPORT size_t
    zsys_pipehwm (void);

Returns the HWM for zactor internal pipes. size_t, naturally.


zsys_set_ipv6</p> 🌈

CZMQ_EXPORT void
    zsys_set_ipv6 (int ipv6);

Toggles IPv6 for new zsock instances. By default, sockets accept and make only IPv4 connections. When you enable IPv6, sockets will accept and connect to both IPv4 and IPv6 peers β€” icon behaviour honestly.

You can override the setting on each zsock_t instance. The default is IPv4 only (ipv6 set to 0). If the environment variable ZSYS_IPV6 is defined (as 1 or 0), that provides the default.

Note: has no effect on ZMQ v2. She's not invited to this party.

zsys_ipv6</p> πŸ’œ

CZMQ_EXPORT int
    zsys_ipv6 (void);

Returns whether IPv6 is in use for zsock instances. An int, classic.


zsys_set_interface</p> πŸŽ€

CZMQ_EXPORT void
    zsys_set_interface (const char *value);

Sets the network interface name to use for broadcasts, particularly zbeacon. This lets the interface be configured for test environments where it's required.

Case in point, on Mac OS X, zbeacon cannot bind to 255.255.255.255, which is the default when there is no specified interface. If the environment variable ZSYS_INTERFACE is set, that gets used as the default interface name.

Setting the interface to "*" means "use all available interfaces". Main character energy, honestly.

zsys_interface</p> πŸ¦‹

CZMQ_EXPORT const char *
    zsys_interface (void);

Returns the network interface used for broadcasts, or "" if none was set. Empty string, not NULL. Respect the distinction.


zsys_set_ipv6_address</p> ✨

CZMQ_EXPORT void
    zsys_set_ipv6_address (const char *value);

Sets the IPv6 address to use for the zbeacon socket, particularly for receiving zbeacon. This needs to be set if IPv6 is enabled, since IPv6 can have multiple addresses on a given interface β€” pick a lane, babe.

If the environment variable ZSYS_IPV6_ADDRESS is set, that gets used as the default IPv6 address.

zsys_ipv6_address</p> 🌸

CZMQ_EXPORT const char *
    zsys_ipv6_address (void);

Returns the IPv6 address to use for zbeacon reception, or "" if none was set.


zsys_set_ipv6_mcast_address</p> πŸ’–

CZMQ_EXPORT void
    zsys_set_ipv6_mcast_address (const char *value);

Sets the IPv6 multicast address used for sending zbeacon messages. This needs to be set if IPv6 is enabled. If the environment variable ZSYS_IPV6_MCAST_ADDRESS is set, that gets used as the default IPv6 multicast address.

zsys_ipv6_mcast_address</p> 🧚

CZMQ_EXPORT const char *
    zsys_ipv6_mcast_address (void);

Returns the IPv6 multicast address used for sending zbeacon, or "" if none was set.


zsys_set_auto_use_fd</p> πŸ”₯

CZMQ_EXPORT void
    zsys_set_auto_use_fd (int auto_use_fd);

Configures the automatic use of pre-allocated FDs when creating new sockets.

If 0 (the default), nothing will happen. Cool, chill, no notes.

Otherwise, when a new socket is bound, the system API will be used to check if an existing pre-allocated FD with a matching port (if TCP) or path (if IPC) exists. And if it does, it will be set via the ZMQ_USE_FD socket option, so that the library uses it instead of creating a new socket. Reuse queen πŸ‘‘

zsys_auto_use_fd</p> ⭐

CZMQ_EXPORT int
    zsys_auto_use_fd (void);

Returns whether automatic pre-allocated FDs are in use for zsock instances. int.


zsys_set_logident</p> 🌈

CZMQ_EXPORT void
    zsys_set_logident (const char *value);

Sets the log identity β€” a string that prefixes all log messages sent by this process. Basically your name tag at the logging party.

The log identity defaults to the environment variable ZSYS_LOGIDENT, if that is set.


zsys_set_logstream</p> πŸ’…

CZMQ_EXPORT void
    zsys_set_logstream (FILE *stream);

Sets the stream to receive log traffic. By default, log traffic is sent to stdout.

If you set the stream to NULL, no stream will receive the log traffic β€” though it may still be sent to the system facility. Sneaky. Still logging, just not to you.


zsys_set_logsender</p> πŸŽ€

CZMQ_EXPORT void
    zsys_set_logsender (const char *endpoint);

Sends log output to a PUB socket bound to the specified endpoint.

To collect such log output, create a SUB socket, subscribe to the traffic you care about, and connect to the endpoint. Log traffic is sent as a single string frame, in the same format as when sent to stdout.

The log system supports a single sender β€” multiple calls to this method will bind the same sender to multiple endpoints. To disable the sender, call this method with a null argument. Ghosted.


zsys_set_logsystem</p> πŸ¦‹

CZMQ_EXPORT void
    zsys_set_logsystem (bool logsystem);

Enables or disables logging to the system facility β€” syslog on POSIX boxes, event log on Windows. By default this is disabled.


Logging, but make it a tier list</p> πŸ”₯

Five functions, five priorities, one vibe check:

  • zsys_error β€” log error condition, highest priority.
CZMQ_EXPORT void
    zsys_error (const char *format, ...);
  • zsys_warning β€” log warning condition, high priority.
CZMQ_EXPORT void
    zsys_warning (const char *format, ...);
  • zsys_notice β€” log normal, but significant, condition, normal priority.
CZMQ_EXPORT void
    zsys_notice (const char *format, ...);
  • zsys_info β€” log informational message, low priority.
CZMQ_EXPORT void
    zsys_info (const char *format, ...);
  • zsys_debug β€” log debug-level message, lowest priority.
CZMQ_EXPORT void
    zsys_debug (const char *format, ...);

All of them take a format plus varargs, so printf-style your way through it, bestie.


zsys_test</p> πŸ§ͺ

CZMQ_EXPORT void
    zsys_test (bool verbose);

Self test of this class. Pass verbose as true if you want the full commentary, false if you want it to just get to the point.


DRAFT METHODS</p> 🌱 (handle with care)

Everything below lives inside:

#ifdef CZMQ_BUILD_DRAFT_API

And every single one of them carries this energy:

* Draft method, for development use, may change without warning *

So like… they're cute, they're useful, they are not a stable commitment. Do not build your whole personality on them.


zsys_is_interrupted</p> πŸ’€

CZMQ_EXPORT bool
    zsys_is_interrupted (void);

* Draft method, for development use, may change without warning *

Checks if the default interrupt handler for Ctrl-C or SIGTERM was called.

Does not work if ZSYS_SIGHANDLER is false and the code does not call set interrupted on signal. Both conditions, no cap.

zsys_set_interrupted</p> πŸ™ˆ

CZMQ_EXPORT void
    zsys_set_interrupted (void);

* Draft method, for development use, may change without warning *

Sets the interrupted flag. This is done by the default signal handler by default β€” however, this can be handy for language bindings or cases without a default signal handler.


zsys_sprintf_hint</p> ✨

CZMQ_EXPORT char *
    zsys_sprintf_hint (int hint, const char *format, ...);

* Draft method, for development use, may change without warning *

Formats a string using printf formatting, returning a freshly allocated buffer. If there was insufficient memory, it returns NULL.

Free the returned string using zstr_free(). Every time. No leaks in this house 🫢

The hinted version allows you to optimize by using a larger starting buffer size (known to, or assumed by, the developer) and so avoid reallocations. Very demure, very mindful.


zsys_set_thread_name_prefix_str</p> 🧡

CZMQ_EXPORT void
    zsys_set_thread_name_prefix_str (const char *prefix);

* Draft method, for development use, may change without warning *

Configures the numeric prefix for each thread created for the internal context's thread pool.

This option is only supported on Linux. Sorry to everyone else.

If the environment variable ZSYS_THREAD_NAME_PREFIX_STR is defined, that provides the default.

⚠️ Note that this method is valid only before any socket is created. Timing is everything, bestie.

zsys_thread_name_prefix_str</p> 🌸

CZMQ_EXPORT const char *
    zsys_thread_name_prefix_str (void);

* Draft method, for development use, may change without warning *

Returns the thread name prefix.


zsys_set_zero_copy_recv</p> πŸ’œ

CZMQ_EXPORT void
    zsys_set_zero_copy_recv (int zero_copy);

* Draft method, for development use, may change without warning *

Configures whether to use the zero copy strategy in libzmq. If the environment variable ZSYS_ZERO_COPY_RECV is defined, that provides the default. Otherwise the default is 1.

zsys_zero_copy_recv</p> ⭐

CZMQ_EXPORT int
    zsys_zero_copy_recv (void);

* Draft method, for development use, may change without warning *

Returns the ZMQ_ZERO_COPY_RECV option. int, as ever.


zsys_set_file_stable_age_msec</p> πŸ•°οΈ

CZMQ_EXPORT void
    zsys_set_file_stable_age_msec (int64_t file_stable_age_msec);

* Draft method, for development use, may change without warning *

Configures the threshold value of filesystem object age per st_mtime that should elapse until we consider that object "stable" at the current zclock_time() moment. It's the "how long do we wait before we trust this file" timer.

The default is S_DEFAULT_ZSYS_FILE_STABLE_AGE_MSEC defined in zsys.c, which generally depends on the host OS, with a fallback value of 5000.

zsys_file_stable_age_msec</p> πŸ¦‹

CZMQ_EXPORT int64_t
    zsys_file_stable_age_msec (void);

* Draft method, for development use, may change without warning *

Returns the current threshold value of file stable age in msec. This can be used in code that chooses to wait for this timeout before testing if a filesystem object is "stable" or not. Patient queen behaviour.


zsys_ipv6_available</p> πŸ”₯

CZMQ_EXPORT bool
    zsys_ipv6_available (void);

* Draft method, for development use, may change without warning *

Tests if IPv6 is available on the system. Returns true if available.

The only way to reliably check is to actually open a socket and try to bind it β€” no shortcuts, no guessing. (Ported from libzmq.)


zsys_set_ipv4_mcast_address</p> 🌈

CZMQ_EXPORT void
    zsys_set_ipv4_mcast_address (const char *value);

* Draft method, for development use, may change without warning *

Sets the IPv4 multicast address to use for sending zbeacon messages.

By default, IPv4 multicast is NOT used. If the environment variable ZSYS_IPV4_MCAST_ADDRESS is set, that gets used as the default IPv4 multicast address.

Calling this function or setting ZSYS_IPV4_MCAST_ADDRESS will enable IPv4 zbeacon messages. Two doors, same room πŸšͺ

okay bestie, we're still deep in the draft zone and I need you to breathe β€” every single one of these is stamped * Draft method, for development use, may change without warning *, so love them, use them, but do not get attached. 🫢

CZMQ_EXPORT const char *
    zsys_ipv4_mcast_address (void);

zsys_ipv4_mcast_address() hands you the IPv4 multicast address you should be using to send zbeacon πŸ’– β€” or it gives you NULL if literally none was ever set. That's it, that's the whole vibe. πŸ¦‹


CZMQ_EXPORT void
    zsys_set_mcast_ttl (byte value);

zsys_set_mcast_ttl() sets the multicast TTL, and the default is 1 in case you were wondering. It takes a byte value. No notes. ✨


CZMQ_EXPORT byte
    zsys_mcast_ttl (void);

zsys_mcast_ttl() is the read-only little sister β€” she just tells you what the multicast TTL currently is, and she returns it as a byte. πŸ‘‘


CZMQ_EXPORT char *
    zsys_zprintf (const char *format, zhash_t *args);

okay so zsys_zprintf() is the moment. πŸ’… It prints a formatted string, except the format is specified by variable names in Python-like format style. So like:

"%(KEY)s=%(VALUE)s", KEY=key, VALUE=value

becomes

"key=value"

periodt. πŸŽ€ It returns a freshly allocated string, or NULL if something went wrong β€” and "wrong" here means not enough memory, invalid format specifier, or name not in args. 🫠

⚠️ Caller owns return value and must destroy it when done. Yes bestie, you have to free it. It's not gonna clean up after itself, no cap. 🧹

CZMQ_EXPORT char *
    zsys_zprintf_error (const char *format, zhash_t *args);

zsys_zprintf_error() is the emotional support function β€” it returns the error string for whatever format/args combination you fed it. Same deal as above: freshly allocated, caller owns it, destroy it when you're done. 🌸


CZMQ_EXPORT char *
    zsys_zplprintf (const char *format, zconfig_t *args);

zsys_zplprintf() is lowkey the same energy as zsys_zprintf(), except it takes a zconfig_t *args instead of a zhash_t *args. πŸ‘€ Same Python-like variable-name format style:

"%(KEY)s=%(VALUE)s", KEY=key, VALUE=value

becomes

"key=value"

Returns a freshly allocated string or NULL on error β€” not enough memory, invalid format specifier, name not in args. 🫠

⚠️ Caller owns return value and must destroy it when done. Same rule, same consequences. πŸŽ€

CZMQ_EXPORT char *
    zsys_zplprintf_error (const char *format, zconfig_t *args);

zsys_zplprintf_error() gives you the error string for the given format/args combo. Caller owns the return value, must destroy it when done. We've been through this, bestie. πŸ’œ


#endif // CZMQ_BUILD_DRAFT_API

and that's the gate closing on all the draft stuff ✨ β€” everything above only exists if you built with the draft API turned on, iykyk.

Please add '@interface' section in './../src/zsys.c'.

^ that's literally in the source, not me being shady. πŸ™ˆ


πŸ’– DESCRIPTION πŸ’–

okay so real talk, what is zsys? πŸ¦‹

The zsys class provides a portable wrapper for system calls. That's the whole mission statement. ✨

We collect all of them here together so we can reduce the number of weird #ifdefs floating around in other classes. Like, instead of every single class doing its own cursed platform-detection gymnastics, they all just call zsys and zsys handles the chaos. πŸ’…

And as far as possible, the bulk of CZMQ classes are fully portable. That's the dream, that's the goal, that's the vibe. πŸ‘‘

Please add @discuss section in ./../src/zsys.c.

yep, still literally in the source. someone left the homework blank bestie. 😭


✨ EXAMPLE ✨

okay bestie, this whole thing is straight out of the zsys_test method β€” like, the actual selftest, no cap. it's giving "everything on this page, but make it a runnable vibe check." πŸ“ grab a drink, this one's long but it ate. πŸ’…

first up: catch those interrupts, peek at capabilities without even using the return value, and then a whole parade of setters doing their thing. the draft-API bit is tucked behind CZMQ_BUILD_DRAFT_API like a secret menu item. πŸ‘‘

zsys_catch_interrupts ();

//  Check capabilities without using the return value
int rc = zsys_has_curve ();

const char *SELFTEST_DIR_RW = "src/selftest-rw";

if (verbose) {
    char *hostname = zsys_hostname ();
    zsys_info ("host name is %s", hostname);
    freen (hostname);
    zsys_info ("system limit is %zu ZeroMQ sockets", zsys_socket_limit ());
}
#ifdef CZMQ_BUILD_DRAFT_API
zsys_set_file_stable_age_msec (5123);
assert (zsys_file_stable_age_msec() == 5123);
zsys_set_file_stable_age_msec (-1);
assert (zsys_file_stable_age_msec() == 5123);
#endif // CZMQ_BUILD_DRAFT_API
zsys_set_linger (0);
zsys_set_sndhwm (1000);
zsys_set_rcvhwm (1000);
zsys_set_pipehwm (2500);
assert (zsys_pipehwm () == 2500);
zsys_set_ipv6 (0);
zsys_set_thread_priority (-1);
zsys_set_thread_sched_policy (-1);
zsys_set_thread_name_prefix (0);
assert (0 == zsys_thread_name_prefix());
assert (streq ("0", zsys_thread_name_prefix_str()));
zsys_thread_affinity_cpu_add (0);
zsys_thread_affinity_cpu_remove (0);
zsys_set_zero_copy_recv(0);
assert (0 == zsys_zero_copy_recv());
zsys_set_zero_copy_recv(1);
assert (1 == zsys_zero_copy_recv());

then it makes a whole pipe, says hi through it, and gets the hi back. main character behavior honestly. πŸ¦‹ after that it goes absolutely feral on the filesystem β€” checking that nosuchfile really doesn't exist (and deleting it if some gremlin left it there πŸ™ˆ), then checking modes and all that.

//  Test pipe creation
zsock_t *pipe_back;
zsock_t *pipe_front = zsys_create_pipe (&pipe_back);
zstr_send (pipe_front, "Hello");
char *string = zstr_recv (pipe_back);
assert (streq (string, "Hello"));
freen (string);
zsock_destroy (&pipe_back);
zsock_destroy (&pipe_front);

//  Test file manipulation

// Don't let anyone fool our workspace
if (zsys_file_exists ("nosuchfile")) {
    zsys_warning ("zsys_test() had to remove 'nosuchfile' which was not expected here at all");
    zsys_file_delete ("nosuchfile");
}

rc = zsys_file_delete ("nosuchfile");
assert (rc == -1);

bool rc_bool = zsys_file_exists ("nosuchfile");
assert (rc_bool != true);

rc = (int) zsys_file_size ("nosuchfile");
assert (rc == -1);

time_t when = zsys_file_modified (".");
assert (when > 0);

int mode = zsys_file_mode (".");
assert (S_ISDIR (mode));
assert (mode & S_IRUSR);
assert (mode & S_IWUSR);

now the dir arc πŸ“ β€” building paths with zsys_sprintf, cleaning up any dirty leftovers from a previous run (because an assert() that dies mid-test leaves the workspace a mess, and we're not about to live like that), and then the file-age check with so much tracing that it's basically narrating its own life. and yes, that's zsys_file_mode_private () and zsys_file_mode_default () bookending the whole thing like the little mode management sandwich it is. πŸ₯ͺ

const char *testbasedir  = ".testsys";
const char *testsubdir  = "subdir";
char *basedirpath = NULL;   // subdir in a test, under SELFTEST_DIR_RW
char *dirpath = NULL;       // subdir in a test, under basedirpath
char *relsubdir = NULL;     // relative short "path" of subdir under testbasedir

basedirpath = zsys_sprintf ("%s/%s", SELFTEST_DIR_RW, testbasedir);
assert (basedirpath);
dirpath = zsys_sprintf ("%s/%s", basedirpath, testsubdir);
assert (dirpath);
relsubdir = zsys_sprintf ("%s/%s", testbasedir, testsubdir);
assert (relsubdir);

// Normally tests clean up in the end, but if a selftest run dies
// e.g. on assert(), workspace remains dirty. Better clean it up.
// We do not really care about results here - we clean up a possible
// dirty exit of an older build. If there are permission errors etc.
// the actual tests below would explode.
if (zsys_file_exists(dirpath)) {
    if (verbose)
        zsys_debug ("zsys_test() has to remove ./%s that should not have been here", dirpath);
    zsys_dir_delete (dirpath);
}
if (zsys_file_exists (basedirpath)) {
    if (verbose)
        zsys_debug ("zsys_test() has to remove ./%s that should not have been here", basedirpath);
    zsys_dir_delete (basedirpath);
}

// Added tracing because this file-age check fails on some systems
// presumably due to congestion in a mass-build and valgrind on top
zsys_file_mode_private ();
if (verbose)
    printf ("zsys_test() at timestamp %" PRIi64 ": "
        "Creating %s\n",
        zclock_time(), relsubdir );
rc = zsys_dir_create ("%s/%s", SELFTEST_DIR_RW, relsubdir);
if (verbose)
    printf ("zsys_test() at timestamp %" PRIi64 ": "
        "Finished creating %s with return-code %d\n",
        zclock_time(), relsubdir, rc );
assert (rc == 0);
when = zsys_file_modified (dirpath);
if (verbose)
    printf ("zsys_test() at timestamp %" PRIi64 ": "
        "Finished calling zsys_file_modified(), got age %jd\n",
        zclock_time(), (intmax_t)when );
assert (when > 0);
if (verbose)
    printf ("zsys_test() at timestamp %" PRIi64 ": "
        "Checking if file is NOT stable (is younger than 1 sec)\n",
        zclock_time() );
assert (!s_zsys_file_stable (dirpath, verbose));
if (verbose)
    printf ("zsys_test() at timestamp %" PRIi64 ": "
        "Passed the test, file is not stable - as expected\n",
        zclock_time() );
rc = zsys_dir_delete ("%s/%s", SELFTEST_DIR_RW, relsubdir);
assert (rc == 0);
rc = zsys_dir_delete ("%s/%s", SELFTEST_DIR_RW, testbasedir);
assert (rc == 0);
zsys_file_mode_default ();

then it wants to know where it is. the way it handles PATH_MAX, _MAX_PATH and a humble 1024 fallback is honestly so real, and it branches for WIN32 with _getcwd vs getcwd like the icon it is. 🌈

#if (defined (PATH_MAX))
char cwd[PATH_MAX];
#else
# if (defined (_MAX_PATH))
char cwd[_MAX_PATH];
# else
char cwd[1024];
# endif
#endif
memset (cwd, 0, sizeof(cwd));
#if (defined (WIN32))
if (_getcwd(cwd, sizeof(cwd)) != NULL) {
#else
if (getcwd(cwd, sizeof(cwd)) != NULL) {
#endif
    if (verbose)
        printf ("zsys_test() at timestamp %" PRIi64 ": "
            "current working directory is %s\n",
            zclock_time(), cwd);
    assert (zsys_dir_change (SELFTEST_DIR_RW) == 0);
    assert (zsys_dir_change (cwd) == 0);
}
else {
    zsys_warning ("zsys_test() : got getcwd() error... "
        "testing zsys_dir_change() anyway, but it can confuse "
        "subsequent tests in this process");
    assert (zsys_dir_change (SELFTEST_DIR_RW) == 0);
}

zstr_free (&basedirpath);
zstr_free (&dirpath);
zstr_free (&relsubdir);

free everything, bestie. cleanliness is next to slayness. 🧚

and now the "other subtests" corner: version numbers, a lil zsys_sprintf with "%s %02x" turning "Hello" and 16 into "Hello 10", and then a str64 banger where strlen has to equal (4 * 64 + 10). math? we do it. ✨

// Other subtests
int major, minor, patch;
zsys_version (&major, &minor, &patch);
assert (major == CZMQ_VERSION_MAJOR);
assert (minor == CZMQ_VERSION_MINOR);
assert (patch == CZMQ_VERSION_PATCH);

string = zsys_sprintf ("%s %02x", "Hello", 16);
assert (streq (string, "Hello 10"));
freen (string);

char *str64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,.";
int num10 = 1234567890;
string = zsys_sprintf ("%s%s%s%s%d", str64, str64, str64, str64, num10);
assert (strlen (string) == (4 * 64 + 10));
freen (string);

logging time!! πŸ“£ it spins up a ZMQ_SUB socket on "inproc://logging", subscribes to literally everything, fires off one of every severity level, then checks that the logsender actually delivered something. obsessed. and then β€” the grand finale β€” the printf block: zsys_zprintf with a zhash, zsys_zprintf_error doing its error-string thing, and zsys_zplprintf reading straight out of a zconfig tree. it's giving ✨ formatting royalty ✨.

//  Test logging system
zsys_set_logident ("czmq_selftest");
zsys_set_logsender ("inproc://logging");
void *logger = zsys_socket (ZMQ_SUB, NULL, 0);
assert (logger);
rc = zmq_connect (logger, "inproc://logging");
assert (rc == 0);
rc = zmq_setsockopt (logger, ZMQ_SUBSCRIBE, "", 0);
assert (rc == 0);

if (verbose) {
    zsys_error ("This is an %s message", "error");
    zsys_warning ("This is a %s message", "warning");
    zsys_notice ("This is a %s message", "notice");
    zsys_info ("This is a %s message", "info");
    zsys_debug ("This is a %s message", "debug");
    zsys_set_logident ("hello, world");
    zsys_info ("This is a %s message", "info");
    zsys_debug ("This is a %s message", "debug");

    //  Check that logsender functionality is working
    char *received = zstr_recv (logger);
    assert (received);
    zstr_free (&received);
}
zsys_close (logger, NULL, 0);

{
    // zhash based printf
    zhash_t *args = zhash_new ();
    zhash_insert (args, "key", "value");
    zhash_insert (args, "ham", "spam");

    char *str = zsys_zprintf ("plain string", args);
    assert (streq (str, "plain string"));
    zstr_free (&str);

    str = zsys_zprintf ("%%a%%", args);
    assert (streq (str, "%a%"));
    zstr_free (&str);

    str = zsys_zprintf ("VALUE=%(key)s123", args);
    assert (streq (str, "VALUE=value123"));
    zstr_free (&str);

    str = zsys_zprintf ("VALUE=%(key)s123, %(ham)s, %(ham)s, %%(nospam)s!!!", args);
    assert (streq (str, "VALUE=value123, spam, spam, %(nospam)s!!!"));
    zstr_free (&str);

    str = zsys_zprintf ("VALUE=%(nokey)s123, %(ham)s, %(ham)s, %%(nospam)s!!!", args);
    assert (!str);

    str = zsys_zprintf_error ("VALUE=%(nokey)s123, %(ham)s, %(ham)s, %%(nospam)s!!!", args);
    assert (streq (str, "Key 'nokey' not found in hash"));
    zstr_free (&str);

    str = zsys_zprintf ("VALUE=%(key)s/%%S", args);
    assert (streq (str, "VALUE=value/%S"));
    zstr_free (&str);

    zhash_destroy (&args);

    //ZPL based printf
    zconfig_t *root = zconfig_new ("root", NULL);
    zconfig_put (root, "zsp", "");
    zconfig_put (root, "zsp/return_code", "0");

    str = zsys_zplprintf ("return_code=%(zsp/return_code)s", root);
    assert (streq (str, "return_code=0"));
    zstr_free (&str);

    zconfig_destroy (&root);
}

πŸ’– AUTHORS πŸ’–

the czmq manual was written by the authors in the AUTHORS file. (yes, they're all in there, go say hi 🫢)


🌈 RESOURCES 🌈

Main web site:

(it's blank right there in the source bestie, not me being shady πŸ™ˆ)

report bugs to the email <zeromq-dev@lists.zeromq.org[1]> πŸ“¬


Copyright (c) the Contributors as noted in the AUTHORS file. This file is part of CZMQ, the high-level C binding for 0MQ: http://czmq.zeromq.org. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. LICENSE included with the czmq distribution.

read the license fr fr, it's right there, it's free, it's giving legally-iconic πŸ’…

πŸ“Ž NOTES πŸ“Ž

  1. zeromq-dev@lists.zeromq.org β€” mailto:zeromq-dev@lists.zeromq.org

and that's a wrap on ZSYS(3), bestie πŸŽ‰ you made it all the way to the end, which is genuinely iconic behavior. this page is from CZMQ 4.2.1, dated 01/16/2026 β€” so go forth, set your hwms, catch your interrupts, and stay stable (or at least, don't be too freshly modified 😭). mwah. πŸ«ΆπŸ’–βœ¨

πŸ“¦ where this page comes from