Sample Code
OSX Driver and Kext Samples/ NotifyTool/ NotifyTool/ NotifyTool.c/
/* File: NotifyTool.c Contains: Sample showing how to use the BSD notify API <x-man-page://3/notify>. Written by: DTS Copyright: Copyright (c) 2007-2012 Apple Inc. All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <assert.h> #include <errno.h> #include <mach/mach.h> #include <notify.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <dispatch/dispatch.h> #include <CoreFoundation/CoreFoundation.h> static void PrintUsage(void) { fprintf(stderr, "usage: %s post <name>...\n", getprogname()); fprintf(stderr, " %*s listen | listenFD <name>...\n", (int) strlen(getprogname()), ""); fprintf(stderr, " %*s listenMach <name>...\n", (int) strlen(getprogname()), ""); fprintf(stderr, " %*s listenCF <name>...\n", (int) strlen(getprogname()), ""); fprintf(stderr, " %*s listenGCD <name>...\n", (int) strlen(getprogname()), ""); } static const char * NotifyErrorToString(uint32_t noteErr) { const char * result; static const char * kErrors[] = { "NOTIFY_STATUS_OK", "NOTIFY_STATUS_INVALID_NAME", "NOTIFY_STATUS_INVALID_TOKEN", "NOTIFY_STATUS_INVALID_PORT", "NOTIFY_STATUS_INVALID_FILE", "NOTIFY_STATUS_INVALID_SIGNAL", "NOTIFY_STATUS_INVALID_REQUEST", "NOTIFY_STATUS_NOT_AUTHORIZED" }; if (noteErr < (sizeof(kErrors) / sizeof(*kErrors))) { result = kErrors[noteErr]; } else if (noteErr == NOTIFY_STATUS_FAILED) { result = "NOTIFY_STATUS_FAILED"; } else { result = "unknown error"; } return result; } static void PrintNotifyError(const char *operation, const char *noteName, uint32_t noteErr) { fprintf( stderr, "%s: %s: %s (%u)\n", noteName, operation, NotifyErrorToString(noteErr), (unsigned int) noteErr ); } static int PostNotifications(size_t noteCount, const char **noteNames) // Implements the "post" command. Post the noteCount notifications whose // names are in the noteNames array. { int retVal; uint32_t noteErr; size_t noteIndex; noteErr = NOTIFY_STATUS_OK; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { noteErr = notify_post(noteNames[noteIndex]); if (noteErr != NOTIFY_STATUS_OK) { break; } } if (noteErr == NOTIFY_STATUS_OK) { retVal = EXIT_SUCCESS; } else { PrintNotifyError("post failed", noteNames[noteIndex], noteErr); retVal = EXIT_FAILURE; } return retVal; } static void PrintToken( int token, size_t noteCount, const int * tokens, const char ** noteNames ) // For a given token, search the tokens array looking for a match. If // you find one, print the associated notification name. If you don't // find one, print a default string. { size_t noteIndex; bool found; found = false; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { if (token == tokens[noteIndex]) { fprintf(stdout, "%s (%d)\n", noteNames[noteIndex], token); found = true; } } if ( ! found ) { fprintf(stdout, "??? (%d)\n", token); } fflush(stdout); } static int ListenUsingFileDescriptor(size_t noteCount, const char **noteNames) // Implements the "listenFD" command. Register for the noteCount // notifications whose names are in the noteNames array. Then read // the notification file descriptor, printing information about any // notifications that arrive. { int retVal; uint32_t noteErr; size_t noteIndex; int noteTokens[noteCount]; int fd = -1; // Register. The first time around this loop fd == -1 and so we don't // specify NOTIFY_REUSE. notify_register_file_descriptor then allocates // a file descriptor and returns it in fd. For subsequent iterations // we /do/ specify NOTIFY_REUSE and notify_register_file_descriptor just // reuses the existing fd. noteErr = NOTIFY_STATUS_OK; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { noteErr = notify_register_file_descriptor( noteNames[noteIndex], &fd, (fd == -1) ? 0 : NOTIFY_REUSE, ¬eTokens[noteIndex] ); if (noteErr != NOTIFY_STATUS_OK) { break; } } if (noteErr != NOTIFY_STATUS_OK) { PrintNotifyError("registration failed", noteNames[noteIndex], noteErr); retVal = EXIT_FAILURE; } else { // Listen for and print any incoming notifications. fprintf(stdout, "Listening using a file descriptor:\n"); fflush(stdout); do { ssize_t bytesRead; int token; bytesRead = read(fd, &token, sizeof(token)); if (bytesRead == 0) { fprintf(stderr, "end of file on notify file descriptor\n"); retVal = EXIT_FAILURE; break; } else if (bytesRead < 0) { fprintf(stderr, "read failed: %s (%d)\n", strerror(errno), errno); retVal = EXIT_FAILURE; break; } else { // I'm just not up for handling partial reads at this point. assert(bytesRead == sizeof(token)); // Have to swap to native endianness <rdar://problem/5352778>. token = ntohl(token); // Find the string associated with this token and print it. PrintToken(token, noteCount, noteTokens, noteNames); } } while (true); } return retVal; } static int ListenUsingMach(size_t noteCount, const char **noteNames) // Implements the "listenMach" command. Register for the noteCount // notifications whose names are in the noteNames array. Then read // the notification Mach port, printing information about any // notifications that arrive. { int retVal; uint32_t noteErr; size_t noteIndex; int noteTokens[noteCount]; mach_port_t port = MACH_PORT_NULL; // Register. The first time around this loop fd == -1 and so we don't // specify NOTIFY_REUSE. notify_register_mach_port then allocates // a Mach port and returns it in port. For subsequent iterations // we /do/ specify NOTIFY_REUSE and notify_register_mach_port just // reuses the existing port. noteErr = NOTIFY_STATUS_OK; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { noteErr = notify_register_mach_port( noteNames[noteIndex], &port, (port == MACH_PORT_NULL) ? 0 : NOTIFY_REUSE, ¬eTokens[noteIndex] ); if (noteErr != NOTIFY_STATUS_OK) { break; } } if (noteErr != NOTIFY_STATUS_OK) { PrintNotifyError("registration failed", noteNames[noteIndex], noteErr); retVal = EXIT_FAILURE; } else { kern_return_t kr; mach_msg_empty_rcv_t msg; // Listen for and print any incoming notifications. fprintf(stdout, "Listening using Mach:\n"); fflush(stdout); do { msg.header.msgh_local_port = port; msg.header.msgh_size = sizeof(msg); kr = mach_msg_receive(&msg.header); if (kr == KERN_SUCCESS) { PrintToken(msg.header.msgh_id, noteCount, noteTokens, noteNames); } } while (kr == KERN_SUCCESS); fprintf(stderr, "error reading Mach message: %s (0x%x)\n", mach_error_string(kr), kr); retVal = EXIT_FAILURE; } return retVal; } struct MyCFMachPortCallBackInfo { OSType magic; // must be 'CFpI' size_t noteCount; const int * noteTokens; const char ** noteNames; }; typedef struct MyCFMachPortCallBackInfo MyCFMachPortCallBackInfo; static void MyCFMachPortCallBack( CFMachPortRef port, void * msg, CFIndex size, void * info ) // The callback associated with the CFMachPort. This get called out of the // runloop when a message arrives on the notification port. We just // extrac the token (msgh_id) and call print it. { #pragma unused(port) #pragma unused(size) const MyCFMachPortCallBackInfo * myInfo; myInfo = (const MyCFMachPortCallBackInfo *) info; assert(myInfo->magic == 'CFpI'); PrintToken( ((const mach_msg_header_t *) msg)->msgh_id, myInfo->noteCount, myInfo->noteTokens, myInfo->noteNames ); } static int ListenUsingCoreFoundation(size_t noteCount, const char **noteNames) // Implements the "listenCF" command. Register for the noteCount // notifications whose names are in the noteNames array. Then wrap the // notification Mach port in a CFMachPort and use CF to read the notification // messages, printing the information about any notifications that arrive // from our CFMachPort callback. { int retVal; uint32_t noteErr; size_t noteIndex; int noteTokens[noteCount]; mach_port_t port = MACH_PORT_NULL; // Register. The first time around this loop fd == -1 and so we don't // specify NOTIFY_REUSE. notify_register_mach_port then allocates // a Mach port and returns it in port. For subsequent iterations // we /do/ specify NOTIFY_REUSE and notify_register_mach_port just // reuses the existing port. noteErr = NOTIFY_STATUS_OK; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { noteErr = notify_register_mach_port( noteNames[noteIndex], &port, (port == MACH_PORT_NULL) ? 0 : NOTIFY_REUSE, ¬eTokens[noteIndex] ); if (noteErr != NOTIFY_STATUS_OK) { break; } } if (noteErr != NOTIFY_STATUS_OK) { PrintNotifyError("registration failed", noteNames[noteIndex], noteErr); retVal = EXIT_FAILURE; } else { MyCFMachPortCallBackInfo myInfo; CFMachPortContext context = { 0 , NULL, NULL, NULL, NULL }; CFMachPortRef cfPort; Boolean shouldFreeInfo; CFRunLoopSourceRef rls; // Set up the context structure for MyCFMachPortCallBack. myInfo.magic = 'CFpI'; myInfo.noteCount = noteCount; myInfo.noteTokens = noteTokens; myInfo.noteNames = noteNames; // Create the CFMachPort. context.info = &myInfo; cfPort = CFMachPortCreateWithPort( NULL, port, MyCFMachPortCallBack, &context, &shouldFreeInfo ); assert(cfPort != NULL); // There can only be one CFMachPort for a given Mach port name. Thus, // if someone had already created a CFMachPort for "port", CFMachPort // would not create a new CFMachPort but, rather, return the existing // CFMachPort with the retain count bumped. In that case it hasn't // taken any 'reference' on the data in context; the context.info // on the /previous/ CFMachPort is still in use, but the context.info // that we supply is now superfluous. In that case it returns // shouldFreeInfo, telling us that we don't need to hold on to this // information. // // In this specific case no one should have already created a CFMachPort // for "port", so shouldFreeInfo should never be true. If it is, it's // time to worry! assert( ! shouldFreeInfo ); // Add it to the run loop. rls = CFMachPortCreateRunLoopSource(NULL, cfPort, 0); assert(rls != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls); // Run the run loop. fprintf(stdout, "Listening using Core Foundation:\n"); fflush(stdout); CFRunLoopRun(); fprintf(stderr, "CFRunLoopRun returned\n"); retVal = EXIT_FAILURE; } return retVal; } static int ListenUsingGCD(size_t noteCount, const char **noteNames) { int retVal; uint32_t noteErr; size_t noteIndex; int noteTokens[noteCount]; const int * noteTokensPtr; // We need to capture the base of the noteTokens array, but the compiler won't let us do that // because it's of variable size. In our specific case this isn't a problem because, if things // go well, we never leave this routine but rather block forever in dispatch_main(). In a real // program you'd have to be a bit more careful (but then again, in a real program you wouldn't be // registering for an unbounded number of arbitrary notifications :-). noteTokensPtr = ¬eTokens[0]; noteErr = NOTIFY_STATUS_OK; for (noteIndex = 0; noteIndex < noteCount; noteIndex++) { noteErr = notify_register_dispatch(noteNames[noteIndex], ¬eTokens[noteIndex], dispatch_get_main_queue(), ^(int token) { PrintToken( token, noteCount, noteTokensPtr, noteNames ); }); if (noteErr != NOTIFY_STATUS_OK) { break; } } if (noteErr != NOTIFY_STATUS_OK) { PrintNotifyError("registration failed", noteNames[noteIndex], noteErr); retVal = EXIT_FAILURE; } else { fprintf(stdout, "Listening using GCD:\n"); fflush(stdout); dispatch_main(); assert(0); // dispatch_main() should never return retVal = EXIT_FAILURE; } return retVal; } int main(int argc, const char **argv) { int retVal; if (argc < 3) { PrintUsage(); retVal = EXIT_FAILURE; } else { if (strcasecmp(argv[1], "post") == 0) { retVal = PostNotifications( ((size_t) argc) - 2, &argv[2]); } else if ((strcasecmp(argv[1], "listen") == 0) || (strcasecmp(argv[1], "listenFD") == 0)) { retVal = ListenUsingFileDescriptor( ((size_t) argc) - 2, &argv[2]); } else if (strcasecmp(argv[1], "listenMach") == 0) { retVal = ListenUsingMach( ((size_t) argc) - 2, &argv[2]); } else if (strcasecmp(argv[1], "listenCF") == 0) { retVal = ListenUsingCoreFoundation( ((size_t) argc) - 2, &argv[2]); } else if (strcasecmp(argv[1], "listenGCD") == 0) { retVal = ListenUsingGCD( ((size_t) argc) - 2, &argv[2]); } else { PrintUsage(); retVal = EXIT_FAILURE; } } return retVal; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials