1 #include "inc.h" 2 3 #define SEM_EVENTS 0x01 /* semaphore code wants process events */ 4 static unsigned int event_mask = 0; 5 6 static int verbose = 0; 7 8 /* 9 * The call table for this service. 10 */ 11 #define CALL(n) [((n) - IPC_BASE)] 12 static int (* const call_vec[])(message *) = { 13 CALL(IPC_SHMGET) = do_shmget, 14 CALL(IPC_SHMAT) = do_shmat, 15 CALL(IPC_SHMDT) = do_shmdt, 16 CALL(IPC_SHMCTL) = do_shmctl, 17 CALL(IPC_SEMGET) = do_semget, 18 CALL(IPC_SEMCTL) = do_semctl, 19 CALL(IPC_SEMOP) = do_semop, 20 }; 21 22 /* 23 * Remote MIB implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. This 24 * function handles all queries on the "kern.ipc.sysvipc_info" sysctl(2) node. 25 */ 26 static ssize_t 27 kern_ipc_info(struct rmib_call * call, struct rmib_node * node __unused, 28 struct rmib_oldp * oldp, struct rmib_newp * newp __unused) 29 { 30 31 if (call->call_namelen != 1) 32 return EINVAL; 33 34 /* 35 * Let each IPC submodule provide information through it own function. 36 * An important security note: unlike IPC_STAT and the like, access to 37 * the sysvipc_info node does not require root privileges. That is: on 38 * NetBSD, any user can get a full listing of all IPC objects in the 39 * system. We therefore do not perform any security check here. 40 */ 41 switch (call->call_name[0]) { 42 case KERN_SYSVIPC_SEM_INFO: 43 return get_sem_mib_info(oldp); 44 45 case KERN_SYSVIPC_SHM_INFO: 46 return get_shm_mib_info(oldp); 47 48 default: 49 return EOPNOTSUPP; 50 } 51 } 52 53 /* The CTL_KERN KERN_SYSVIPC subtree. */ 54 static struct rmib_node kern_ipc_table[] = { 55 /* 1*/ [KERN_SYSVIPC_INFO] = RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0, 56 kern_ipc_info, "sysvipc_info", 57 "System V style IPC information"), 58 /* 2*/ [KERN_SYSVIPC_MSG] = RMIB_INT(RMIB_RO, 0, "sysvmsg", "System V " 59 "style message support available"), 60 /* 3*/ [KERN_SYSVIPC_SEM] = RMIB_INT(RMIB_RO, 1, "sysvsem", "System V " 61 "style semaphore support available"), 62 /* 4*/ [KERN_SYSVIPC_SHM] = RMIB_INT(RMIB_RO, 1, "sysvshm", "System V " 63 "style shared memory support available"), 64 /* 5*/ /* KERN_SYSVIPC_SHMMAX: not yet supported */ 65 /* 6*/ /* KERN_SYSVIPC_SHMMNI: not yet supported */ 66 /* 7*/ /* KERN_SYSVIPC_SHMSEG: not yet supported */ 67 /* 8*/ /* KERN_SYSVIPC_SHMMAXPGS: not yet supported */ 68 /* 9*/ /* KERN_SYSVIPC_SHMUSEPHYS: not yet supported */ 69 /* In addition, NetBSD has a number of dynamic nodes here. */ 70 }; 71 72 /* The CTL_KERN KERN_SYSVIPC node. */ 73 static struct rmib_node kern_ipc_node = 74 RMIB_NODE(RMIB_RO, kern_ipc_table, "ipc", "SysV IPC options"); 75 76 /* 77 * Initialize the IPC server. 78 */ 79 static int 80 sef_cb_init_fresh(int type __unused, sef_init_info_t * info __unused) 81 { 82 const int mib[] = { CTL_KERN, KERN_SYSVIPC }; 83 int r; 84 85 /* 86 * Register our own "kern.ipc" subtree with the MIB service. 87 * 88 * This call only returns local failures. Remote failures (in the MIB 89 * service) are silently ignored. So, we can safely panic on failure. 90 */ 91 if ((r = rmib_register(mib, __arraycount(mib), &kern_ipc_node)) != OK) 92 panic("unable to register remote MIB tree: %d", r); 93 94 return OK; 95 } 96 97 /* 98 * The service has received a signal. 99 */ 100 static void 101 sef_cb_signal_handler(int signo) 102 { 103 104 /* Only check for termination signal, ignore anything else. */ 105 if (signo != SIGTERM) return; 106 107 /* 108 * Check if there are still IPC keys around. If not, we can safely 109 * exit immediately. Otherwise, warn the system administrator. 110 */ 111 if (is_sem_nil() && is_shm_nil()) { 112 rmib_deregister(&kern_ipc_node); 113 114 sef_exit(0); 115 } 116 117 printf("IPC: exit with unclean state\n"); 118 } 119 120 /* 121 * Perform SEF initialization. 122 */ 123 static void 124 sef_local_startup(void) 125 { 126 127 /* Register init callbacks. */ 128 sef_setcb_init_fresh(sef_cb_init_fresh); 129 sef_setcb_init_restart(sef_cb_init_fresh); 130 131 /* Register signal callbacks. */ 132 sef_setcb_signal_handler(sef_cb_signal_handler); 133 134 /* Let SEF perform startup. */ 135 sef_startup(); 136 } 137 138 /* 139 * Update the process event subscription mask if necessary, after one of the 140 * modules has changed its subscription needs. This code is set up so that 141 * support for SysV IPC message queues can be added easily later. 142 */ 143 static void 144 update_sub(unsigned int new_mask) 145 { 146 147 /* If the old and new mask are not both zero or nonzero, update. */ 148 if (!event_mask != !new_mask) { 149 /* 150 * Subscribe to PM process events, or unsubscribe. While it 151 * might be tempting to implement a system that subscribes to 152 * events only from processes that are actually blocked (or 153 * using the SysV IPC facilities at all), this would result in 154 * race conditions where subscription could happen "too late" 155 * for an ongoing signal delivery, causing the affected process 156 * to deadlock. Subscribing to events from any other call is 157 * safe however, and we exploit that to limit the kernel-level 158 * message passing overhead in the common case (which is that 159 * the IPC servier is not being used at all). After we have 160 * unsubscribed, we may still get a few leftover events for the 161 * previous subscription, and we must properly reply to those. 162 */ 163 if (new_mask) 164 proceventmask(PROC_EVENT_EXIT | PROC_EVENT_SIGNAL); 165 else 166 proceventmask(0); 167 } 168 169 event_mask = new_mask; 170 } 171 172 /* 173 * Update the process event subscription mask for the semaphore code. 174 */ 175 void 176 update_sem_sub(int want_events) 177 { 178 unsigned int new_mask; 179 180 new_mask = event_mask & ~SEM_EVENTS; 181 if (want_events) 182 new_mask |= SEM_EVENTS; 183 184 update_sub(new_mask); 185 } 186 187 /* 188 * PM sent us a process event message. Handle it, and reply. 189 */ 190 static void 191 got_proc_event(message * m) 192 { 193 endpoint_t endpt; 194 int r, has_exited; 195 196 endpt = m->m_pm_lsys_proc_event.endpt; 197 has_exited = (m->m_pm_lsys_proc_event.event == PROC_EVENT_EXIT); 198 199 /* 200 * Currently, only semaphore handling needs to know about processes 201 * being signaled and exiting. 202 */ 203 if (event_mask & SEM_EVENTS) 204 sem_process_event(endpt, has_exited); 205 206 /* Echo the request as a reply back to PM. */ 207 m->m_type = PROC_EVENT_REPLY; 208 if ((r = asynsend3(m->m_source, m, AMF_NOREPLY)) != OK) 209 printf("IPC: replying to PM process event failed (%d)\n", r); 210 } 211 212 /* 213 * The System V IPC server. 214 */ 215 int 216 main(int argc, char ** argv) 217 { 218 message m; 219 unsigned int call_index; 220 int r, ipc_status; 221 222 /* SEF local startup. */ 223 env_setargs(argc, argv); 224 sef_local_startup(); 225 226 /* The main message loop. */ 227 for (;;) { 228 if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) 229 panic("IPC: sef_receive_status failed: %d", r); 230 231 if (verbose) 232 printf("IPC: got %d from %d\n", m.m_type, m.m_source); 233 234 if (is_ipc_notify(ipc_status)) { 235 printf("IPC: ignoring notification from %d\n", 236 m.m_source); 237 continue; 238 } 239 240 /* Process event messages from PM are handled separately. */ 241 if (m.m_source == PM_PROC_NR && m.m_type == PROC_EVENT) { 242 got_proc_event(&m); 243 244 continue; 245 } 246 247 /* Remote MIB messages from MIB are handled separately too. */ 248 if (m.m_source == MIB_PROC_NR) { 249 rmib_process(&m, ipc_status); 250 251 continue; 252 } 253 254 /* Dispatch the request. */ 255 call_index = (unsigned int)(m.m_type - IPC_BASE); 256 257 if (call_index < __arraycount(call_vec) && 258 call_vec[call_index] != NULL) { 259 r = call_vec[call_index](&m); 260 } else 261 r = ENOSYS; 262 263 /* Send a reply, if needed. */ 264 if (r != SUSPEND) { 265 if (verbose) 266 printf("IPC: call result %d\n", r); 267 268 m.m_type = r; 269 /* 270 * Other fields may have been set by the handler 271 * function already. 272 */ 273 274 if ((r = ipc_sendnb(m.m_source, &m)) != OK) 275 printf("IPC: send error %d\n", r); 276 } 277 278 /* XXX there must be a better way to do this! */ 279 update_refcount_and_destroy(); 280 } 281 282 /* NOTREACHED */ 283 return 0; 284 } 285