This chapter describes how to send and receive Darwin notifications. The Darwin notification system is relatively straightforward for developers familiar with programming in other UNIX/Linux operating systems. It ties into commonly used systems such as file descriptors and signals to provide delivery of messages to the client process.
Darwin notifications are supported by the notifyd(8)
daemon, a process that listens for incoming notifications and redelivers those notifications to interested processes in a variety of ways.
When writing a tool that uses Darwin notifications, the following headers are commonly used:
#include <unistd.h> // good idea in general |
#include <stdlib.h> // good idea in general |
#include <strings.h> // for bcopy, used by FD_COPY macro |
// (file descriptor delivery) |
#include <sys/select.h> // for select (file descriptors delivery) |
#include <stdio.h> // for read (file descriptor delivery) |
#include <signal.h> // for signal names (signal delivery) |
#include <mach/message.h> // For mach message functions and types |
// (mach message delivery) |
#include <notify.h> // for all notifications |
Sending a Darwin notification is very simple. Just call the function notify_post
. The function takes a single argument that contains the name of the notification. (See “How Should Notifications Be Named?” for information about notification naming.)
For example, if you integrate notifications into the Apache web server and want to notify it that its configuration file has changed, you might send the notification with a call like the following:
if (notify_post("org.apache.httpd.configFileChanged")) { |
printf("Notification failed.\n"); exit(-1); |
} |
The function notify_post
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference.
Darwin notifications provide five mechanisms for receiving notifications: Core Foundation, file descriptors, signals, Mach messages, and manual polling. This section describes these delivery mechanisms and explains when you should choose each one.
When adding notification support to existing applications, you should choose whichever mechanism is easiest to integrate into the application or tool. If your application already uses signal handling, you should use signals. If your application already uses sockets or file descriptors, you should use file descriptors. And so on.
If you are writing a new application from scratch, Core Foundation notifications are recommended if your application is based on Core Foundation. For non-Core Foundation applications, file descriptors are the preferred notification transport because they are more robust against message loss than signals and are easier to use than Mach messages. Again, you should choose the mechanism that most closely fits the architecture of the tool you are writing.
As a general rule, you should avoid using the polling interface (described in “Receiving Notifications Manually”). You should use polling only if you need to check for a status change infrequently.
To register for Darwin notifications using Core Foundation, first create a notification center by calling CFNotificationCenterGetDarwinNotifyCenter
. This creates an object that interacts with the notification daemon on your behalf.
Next, call CFNotificationCenterAddObserver
. This function instructs the notification center to register for notifications with the notification daemon.
The following snippet shows how to register for the notification described in “Sending Notifications”:
/* This function is called when a notification is received. */ |
void MyNotificationCenterCallBack(CFNotificationCenterRef center, |
void *observer, |
CFStringRef name, |
const void *object, |
CFDictionaryRef userInfo) |
{ |
printf("Notification center handler called\n"); |
} |
... |
/* Create a notification center */ |
CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter(); |
/* Tell notifyd to alert us when this notification |
is received. */ |
if (center) { |
CFNotificationCenterAddObserver(center, |
NULL, |
MyNotificationCenterCallBack, |
CFSTR("org.apache.httpd.configFileChanged"), |
NULL, |
CFNotificationSuspensionBehaviorDeliverImmediately); |
... |
} |
Receiving notifications with file descriptors is relatively straightforward. First, you must call notify_register_file_descriptor
to register for notifications. For example, the following snippet shows how to register for the notification sent in “Sending Notifications”:
int fd; /* file descriptor---one per process if |
NOTIFY_REUSE is set, else one per name */ |
int notification_token; /* notification token---one per name */ |
... |
if (notify_register_file_descriptor("org.apache.httpd.configFileChanged", |
&fd, |
0, |
¬ification_token)) { |
/* Something went wrong. Bail. */ |
printf("Registration failed.\n"); exit(-1); |
} |
The function notify_register_file_descriptor
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference.
Note: The flag field is set to zero (0
) in this call. You must not set the flag NOTIFY_REUSE
until the second and subsequent calls to notify_register_file_descriptor
. This flag tells the notification library code to reuse the existing file descriptor. If you use this flag, your code will not work correctly unless the file descriptor value has been initialized by a previous call to notify_register_file_descriptor
.
After you register for notification, you will begin receiving data through the returned file descriptor. You can detect the arrival of new data using select(2)
or poll(2)
, as shown in the following snippet:
fd_set receive_descriptors, receive_descriptors_copy; |
FD_SET(fd, /* from call to notify_register_file_descriptor */ |
&receive_descriptors); |
FD_COPY(&receive_descriptors, &receive_descriptors_copy); |
while (select(fd + 1, &receive_descriptors_copy, |
NULL, NULL, NULL) >= 0) { |
/* Data was received. */ |
if (FD_ISSET(fd, &receive_descriptors_copy)) { |
/* Data was received on the right descriptor. |
Do something. */ |
int token; |
/*! Read four bytes from the file descriptor. */ |
if (read(fd, &token, sizeof(token)) != sizeof(token)) { |
/* An error occurred. Panic. */ |
printf("Read error on descriptor. Exiting.\n"); |
exit(-1); |
} |
/* At this point, the value in token should match one of the |
registration tokens returned through the fourth parameter |
of a previous call to notify_register_file_descriptor. */ |
} |
FD_COPY(&receive_descriptors, &receive_descriptors_copy); |
} |
For more information about the select
system call, see the manual page for select(2)
.
To receive a signal when a new notification is posted, call the function notify_register_signal
. This function tells the notification daemon to send a signal to your process whenever it posts new messages.
Note: Although you can choose what signal notifyd
sends to your process, some signals have special purposes and should not be used for notifications.
The SIGALRM
, SIGVTALRM
, and SIGTHR
(P_SIGTHR
) signals are used for other purposes related to thread scheduling and sleep(1)
/usleep(3)
/nanosleep(2)
timers. If you use these signals for notifications, unexpected behavior may result.
The SIGKILL
signal cannot be trapped and thus cannot be used for notifications (unless your goal is to terminate your application when the event occurs).
The following snippet shows how to register for the notification described in “Sending Notifications”:
int notification_token; |
... |
/* Set up a signal handler for SIGHUP */ |
signal(SIGHUP, &my_sighup_handler); |
/* Tell notifyd to send SIGHUP when this notification |
is received. */ |
if (notify_register_signal( |
"org.apache.httpd.configFileChanged", SIGHUP, |
¬ification_token)) { |
printf("Registration failed.\n"); exit(-1); |
} |
The function notify_register_signal
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference.
The value of notification_token
is set to an integer value specific to the name for this notification. It is your responsibility to keep track of these notification token values so that you can find out which notification was posted (unless you are registering only for a single notification and don’t care about false positives caused by signals not generated by notifyd
).
Portability Note: In Mac OS X, signal handlers are not cleared when a signal handler returns. However, for maximum portability, you should still get in the habit of calling signal
at the end of your signal-handler function. This will ensure that signal-handling code is portable across operating system environments.
After you register to receive a signals, you must then call notify_check
to determine which (if any) notification triggered the signal, as shown in the next snippet:
int was_posted; |
if (notify_check(notification_token, &was_posted)) { |
printf("Call to notify_check failed.\n"); exit(-1); |
} |
if (was_posted) { |
/* The notification org.apache.httpd.configFileChanged |
was posted. */ |
} |
The function notify_check
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference. The function returns a value through the second parameter to indicate whether the notification has been posted since the last time you called notify_check
.
If your tool or application uses Mach messages for communication, you may find it convenient to use Mach messages for receiving notification. Communication based on Mach messaging is not recommended for use in new designs because it is relatively easy to corrupt your application's memory if used incorrectly.
Note: This section assumes that you are already familiar with Mach IPC concepts such as ports and port rights. Thus, it does not cover these concepts in any great detail. For more information, see Mach Overview.
You can register for Mach message–based notification on a Mach port by calling notify_register_mach_port
, as shown in the snippet below:
mach_port_name_t port; /* mach port---one per process if |
NOTIFY_REUSE is set, else one per name */ |
int notification_token; /* notification token---one per name */ |
/* Allocate a mach port. If you already have receive rights on a |
port and would prefer to use that, you can do so, of course. */ |
if (mach_port_allocate (mach_task_self(), |
MACH_PORT_RIGHT_RECEIVE, &port) != KERN_SUCCESS) { |
printf("Could not allocate mach port.\n"); exit(-1); |
} |
if (notify_register_mach_port("org.apache.httpd.configFileChanged", |
&port, |
NOTIFY_REUSE, |
¬ification_token)) { |
/* Something went wrong. Bail. */ |
printf("Registration failed.\n"); exit(-1); |
} |
The function notify_register_mach_port
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference.
If desired, you can let Mach allocate the port for you by skipping the call to mach_port_allocate
and passing in 0
instead of NOTIFY_REUSE
.
Note: You must not set the flag NOTIFY_REUSE
without allocating a port first. If you do not allocate a port in advance, you must not use the NOTIFY_REUSE
flag until the second and subsequent calls to notify_register_file_descriptor
. If you do, your application will probably crash.
This flag tells the notification library code to reuse the existing Mach port. The call will fail unless the Mach port has been initialized and your Mach task holds receive rights on the port.
After you have registered for notification, you can receive messages on the port with the mach_msg_overwrite
call, as shown in the following snippet:
struct { |
mach_msg_header_t hdr; |
int token; |
} mydatastructure; |
while (1) { |
mach_msg_overwrite(NULL, MACH_RCV_MSG, |
0, sizeof(mydatastructure), port, MACH_MSG_TIMEOUT_NONE, |
MACH_PORT_NULL, (mach_msg_header_t *)&mydatastructure, |
sizeof(mydatastructure.token)); |
printf("Data received : %d (compare to %d).\n", mydatastructure.token, notification_token); |
} |
Although it is not common to do so, you may sometimes find it useful to poll to see (on an occasional basis) whether a particular notification has occurred. To do this, you must request a token corresponding to the notification name by calling notify_register_check
, as shown in the following snippet:
if (notify_register_check( |
"org.apache.httpd.configFileChanged", ¬ification_token)) { |
printf("Registration failed.\n"); exit(-1); |
} |
You can then check for notifications using notify_check
(just as you would for signal delivery), as shown in the following snippet:
int was_posted; |
while (1) { |
sleep(1); |
if (notify_check(notification_token, &was_posted)) { |
printf("Call to notify_check failed.\n"); exit(-1); |
} |
if (was_posted) { |
/* The notification org.apache.httpd.configFileChanged |
was posted. */ |
printf("Notification %d was posted.\n", notification_token); |
} |
} |
Note: Due to limitations in the underlying architecture,
you may get a "false positive" result on the initial call to notify_check
when using this method.
The function notify_check
returns zero on success or an error code on failure. The possible error codes are described in Status Codes in Darwin Notification API Reference. The function returns a value through the second parameter to indicate whether the notification has been posted since the last time you called notify_check
.
Last updated: 2009-05-01