void setup_notifyd() { #if FISH_NOTIFYD_AVAILABLE // per notify(3), the user.uid.%d style is only accessible to processes with that uid char local_name[256]; snprintf(local_name, sizeof local_name, "user.uid.%d.%ls.uvars", getuid(), program_name ? program_name : L"fish"); name.assign(local_name); uint32_t status = notify_register_file_descriptor(name.c_str(), &this->notify_fd, 0, &this->token); if (status != NOTIFY_STATUS_OK) { fprintf(stderr, "Warning: notify_register_file_descriptor() failed with status %u. Universal variable notifications may not be received.", status); } if (this->notify_fd >= 0) { // Mark us for non-blocking reads, with CLO_EXEC int flags = fcntl(this->notify_fd, F_GETFL, 0); fcntl(this->notify_fd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC); } #endif }
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; }
int runDaemonRun(void) { // Register for all the notifications int fd; int quitToken = 0; if (notify_register_file_descriptor(quitNotification, &fd, 0, &quitToken) != NOTIFY_STATUS_OK) { return 0x10; } int startToken = 0; if (notify_register_file_descriptor(startNotification, &fd, NOTIFY_REUSE, &startToken) != NOTIFY_STATUS_OK) { return 0x11; } int stopToken = 0; if (notify_register_file_descriptor(stopNotification, &fd, NOTIFY_REUSE, &stopToken) != NOTIFY_STATUS_OK) { return 0x12; } int debugToken = 0; if (notify_register_file_descriptor(debugNotification, &fd, NOTIFY_REUSE, &debugToken) != NOTIFY_STATUS_OK) { return 0x14; } print_message(0, "Started as %d(%d):%d(%d)\n", getuid(), geteuid(), getgid(), getegid()); fd_set readfds; FD_ZERO(&readfds); FD_SET(fd, &readfds); pid_t ngircdPid = 0; int ps; char debug = 1; char shouldContinue = 1; while (shouldContinue) { int status = select(fd + 1, &readfds, NULL, NULL, NULL); if (status <= 0 || !FD_ISSET(fd, &readfds)) { continue; } int t; status = read(fd, &t, sizeof(int)); if (status < 0) { continue; } t = ntohl(t); // notify_register_file_descriptor docs: "The value is sent in network byte order." // Value in file descriptor matches token for quit notification if (t == quitToken) { if (debug) print_message(0, "Quitting\n"); shouldContinue = 0; } // Value in file descriptor matches token for start notification if (t == startToken) { if (debug) print_message(0, "Starting\n"); // Start ngircd as non-daemon, using custom config file const char *args[] = {"ngircd", "-n", "-f", "/Library/Application Support/IRCyslog/ircyslog.conf", NULL}; if (!ngircdPid) { ps = posix_spawn(&ngircdPid, "/usr/sbin/ngircd", NULL, NULL, (char *const *)args, NULL); if (!ps) { if (debug) print_message(0, "Started ngircd with pid:%d\n", ngircdPid); } } // Start the IRC bot // ... } // Value in file descriptor matches token for stop notification // or the quit notification was posted if (t == stopToken || !shouldContinue) { if (debug) print_message(0, "stopping"); // Stop the IRC bot // ... if (!ps) { // If the server is running, kill it if (debug) print_message(0, "Calling kill with (%d, %d)\n", ngircdPid, SIGTERM); int k = kill(ngircdPid, SIGTERM); if (debug) print_message(0, "kill'd with exit status %d\n", k); // Wait for the server to finish and purge from the process list int st; if (debug) print_message(0, "Calling waitpid with (%d,%d)\n", ngircdPid, 0); int w = waitpid(ngircdPid, &st, 0); if (debug) print_message(0, "waitpid'd with exit status %d, stat_loc %d\n", w, st); } } // Value in file descriptor matches token for debug notification if (t == debugToken) { debug = !debug; print_message(0, "debug is %s\n", debug ? "ON" : "OFF"); } } // Cancel notification watching print_message(0, "Cancelling notifications\n"); notify_cancel(quitToken); notify_cancel(startToken); notify_cancel(stopToken); notify_cancel(debugToken); return 0; }