1/* 2 * Copyright (c) 2000, 2001, 2004, 2006, 2009-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * June 1, 2001 Allan Nathanson <ajn@apple.com> 28 * - public API conversion 29 * 30 * March 31, 2000 Allan Nathanson <ajn@apple.com> 31 * - initial revision 32 */ 33 34#include <mach/mach.h> 35#include <mach/mach_error.h> 36 37#include <SystemConfiguration/SystemConfiguration.h> 38#include <SystemConfiguration/SCPrivate.h> 39#include "SCDynamicStoreInternal.h" 40#include "config.h" /* MiG generated file */ 41 42static mach_msg_id_t 43waitForMachMessage(mach_port_t port) 44{ 45 union { 46 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE]; 47 mach_msg_empty_rcv_t msg; 48 } notify_msg; 49 kern_return_t status; 50 51 status = mach_msg(¬ify_msg.msg.header, /* msg */ 52 MACH_RCV_MSG, /* options */ 53 0, /* send_size */ 54 sizeof(notify_msg), /* rcv_size */ 55 port, /* rcv_name */ 56 MACH_MSG_TIMEOUT_NONE, /* timeout */ 57 MACH_PORT_NULL); /* notify */ 58 if (status != KERN_SUCCESS) { 59 SCLog(TRUE, LOG_DEBUG, CFSTR("waitForMachMessage mach_msg(): %s"), mach_error_string(status)); 60 return -1; 61 } 62 63 return notify_msg.msg.header.msgh_id; 64} 65 66 67Boolean 68SCDynamicStoreNotifyWait(SCDynamicStoreRef store) 69{ 70 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; 71 kern_return_t status; 72 mach_port_t port; 73 mach_port_t oldNotify; 74 int sc_status; 75 mach_msg_id_t msgid; 76 77 if (store == NULL) { 78 /* sorry, you must provide a session */ 79 _SCErrorSet(kSCStatusNoStoreSession); 80 return FALSE; 81 } 82 83 if (storePrivate->server == MACH_PORT_NULL) { 84 /* sorry, you must have an open session to play */ 85 _SCErrorSet(kSCStatusNoStoreServer); 86 return FALSE; 87 } 88 89 if (storePrivate->notifyStatus != NotifierNotRegistered) { 90 /* sorry, you can only have one notification registered at once */ 91 _SCErrorSet(kSCStatusNotifierActive); 92 return FALSE; 93 } 94 95 /* Allocating port (for server response) */ 96 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 97 if (status != KERN_SUCCESS) { 98 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyWait mach_port_allocate(): %s"), mach_error_string(status)); 99 _SCErrorSet(status); 100 return FALSE; 101 } 102 103 status = mach_port_insert_right(mach_task_self(), 104 port, 105 port, 106 MACH_MSG_TYPE_MAKE_SEND); 107 if (status != KERN_SUCCESS) { 108 /* 109 * We can't insert a send right into our own port! This should 110 * only happen if someone stomped on OUR port (so let's leave 111 * the port alone). 112 */ 113 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyWait mach_port_insert_right(): %s"), mach_error_string(status)); 114 _SCErrorSet(status); 115 return FALSE; 116 } 117 118 /* Request a notification when/if the server dies */ 119 status = mach_port_request_notification(mach_task_self(), 120 port, 121 MACH_NOTIFY_NO_SENDERS, 122 1, 123 port, 124 MACH_MSG_TYPE_MAKE_SEND_ONCE, 125 &oldNotify); 126 if (status != KERN_SUCCESS) { 127 /* 128 * We can't request a notification for our own port! This should 129 * only happen if someone stomped on OUR port (so let's leave 130 * the port alone). 131 */ 132 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyWait mach_port_request_notification(): %s"), mach_error_string(status)); 133 _SCErrorSet(status); 134 return FALSE; 135 } 136 137 if (oldNotify != MACH_PORT_NULL) { 138 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyWait(): oldNotify != MACH_PORT_NULL")); 139 } 140 141 retry : 142 143 status = notifyviaport(storePrivate->server, 144 port, 145 0, 146 (int *)&sc_status); 147 148 if (__SCDynamicStoreCheckRetryAndHandleError(store, 149 status, 150 &sc_status, 151 "SCDynamicStoreNotifyWait notifyviaport()")) { 152 goto retry; 153 } 154 155 if (status != KERN_SUCCESS) { 156 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { 157 /* remove the send right that we tried (but failed) to pass to the server */ 158 (void) mach_port_deallocate(mach_task_self(), port); 159 } 160 161 /* remove our receive right */ 162 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); 163 } 164 165 if (sc_status != kSCStatusOK) { 166 _SCErrorSet(sc_status); 167 return FALSE; 168 } 169 170 /* set notifier active */ 171 storePrivate->notifyStatus = Using_NotifierWait; 172 173 msgid = waitForMachMessage(port); 174 175 /* set notifier inactive */ 176 storePrivate->notifyStatus = NotifierNotRegistered; 177 178 if (msgid == MACH_NOTIFY_NO_SENDERS) { 179 /* the server closed the notifier port */ 180#ifdef DEBUG 181 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait notifier port closed, destroying port %d"), port); 182#endif /* DEBUG */ 183 _SCErrorSet(kSCStatusNoStoreServer); 184 return FALSE; 185 } 186 187 if (msgid == -1) { 188 /* one of the mach routines returned an error */ 189#ifdef DEBUG 190 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait communication with server failed, destroying port %d"), port); 191#endif /* DEBUG */ 192 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE , -1); 193 _SCErrorSet(kSCStatusNoStoreServer); 194 return FALSE; 195 } 196 197 // something changed, cancelling notification request 198 status = notifycancel(storePrivate->server, 199 (int *)&sc_status); 200 201 if (__SCDynamicStoreCheckRetryAndHandleError(store, 202 status, 203 &sc_status, 204 "SCDynamicStoreNotifyWait notifycancel()")) { 205 sc_status = kSCStatusOK; 206 } 207 208 /* remove our receive right */ 209 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE , -1); 210 211 if (sc_status != kSCStatusOK) { 212 _SCErrorSet(sc_status); 213 return FALSE; 214 } 215 216 return TRUE; 217} 218