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 * Initialize the IPC server. 24 */ 25 static int 26 sef_cb_init_fresh(int type __unused, sef_init_info_t * info __unused) 27 { 28 29 /* Nothing to do. */ 30 return OK; 31 } 32 33 /* 34 * The service has received a signal. 35 */ 36 static void 37 sef_cb_signal_handler(int signo) 38 { 39 40 /* Only check for termination signal, ignore anything else. */ 41 if (signo != SIGTERM) return; 42 43 /* 44 * Check if there are still IPC keys around. If not, we can safely 45 * exit immediately. Otherwise, warn the system administrator. 46 */ 47 if (is_sem_nil() && is_shm_nil()) 48 sef_exit(0); 49 50 printf("IPC: exit with unclean state\n"); 51 } 52 53 /* 54 * Perform SEF initialization. 55 */ 56 static void 57 sef_local_startup(void) 58 { 59 60 /* Register init callbacks. */ 61 sef_setcb_init_fresh(sef_cb_init_fresh); 62 sef_setcb_init_restart(sef_cb_init_fresh); 63 64 /* Register signal callbacks. */ 65 sef_setcb_signal_handler(sef_cb_signal_handler); 66 67 /* Let SEF perform startup. */ 68 sef_startup(); 69 } 70 71 /* 72 * Update the process event subscription mask if necessary, after one of the 73 * modules has changed its subscription needs. This code is set up so that 74 * support for SysV IPC message queues can be added easily later. 75 */ 76 static void 77 update_sub(unsigned int new_mask) 78 { 79 80 /* If the old and new mask are not both zero or nonzero, update. */ 81 if (!event_mask != !new_mask) { 82 /* 83 * Subscribe to PM process events, or unsubscribe. While it 84 * might be tempting to implement a system that subscribes to 85 * events only from processes that are actually blocked (or 86 * using the SysV IPC facilities at all), this would result in 87 * race conditions where subscription could happen "too late" 88 * for an ongoing signal delivery, causing the affected process 89 * to deadlock. Subscribing to events from any other call is 90 * safe however, and we exploit that to limit the kernel-level 91 * message passing overhead in the common case (which is that 92 * the IPC servier is not being used at all). After we have 93 * unsubscribed, we may still get a few leftover events for the 94 * previous subscription, and we must properly reply to those. 95 */ 96 if (new_mask) 97 proceventmask(PROC_EVENT_EXIT | PROC_EVENT_SIGNAL); 98 else 99 proceventmask(0); 100 } 101 102 event_mask = new_mask; 103 } 104 105 /* 106 * Update the process event subscription mask for the semaphore code. 107 */ 108 void 109 update_sem_sub(int want_events) 110 { 111 unsigned int new_mask; 112 113 new_mask = event_mask & ~SEM_EVENTS; 114 if (want_events) 115 new_mask |= SEM_EVENTS; 116 117 update_sub(new_mask); 118 } 119 120 /* 121 * PM sent us a process event message. Handle it, and reply. 122 */ 123 static void 124 got_proc_event(message * m) 125 { 126 endpoint_t endpt; 127 int r, has_exited; 128 129 endpt = m->m_pm_lsys_proc_event.endpt; 130 has_exited = (m->m_pm_lsys_proc_event.event == PROC_EVENT_EXIT); 131 132 /* 133 * Currently, only semaphore handling needs to know about processes 134 * being signaled and exiting. 135 */ 136 if (event_mask & SEM_EVENTS) 137 sem_process_event(endpt, has_exited); 138 139 /* Echo the request as a reply back to PM. */ 140 m->m_type = PROC_EVENT_REPLY; 141 if ((r = asynsend3(m->m_source, m, AMF_NOREPLY)) != OK) 142 printf("IPC: replying to PM process event failed (%d)\n", r); 143 } 144 145 /* 146 * The System V IPC server. 147 */ 148 int 149 main(int argc, char ** argv) 150 { 151 message m; 152 unsigned int call_index; 153 int r, ipc_status; 154 155 /* SEF local startup. */ 156 env_setargs(argc, argv); 157 sef_local_startup(); 158 159 /* The main message loop. */ 160 for (;;) { 161 if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) 162 panic("IPC: sef_receive_status failed: %d", r); 163 164 if (verbose) 165 printf("IPC: got %d from %d\n", m.m_type, m.m_source); 166 167 if (is_ipc_notify(ipc_status)) { 168 printf("IPC: ignoring notification from %d\n", 169 m.m_source); 170 continue; 171 } 172 173 /* Process event messages from PM are handled separately. */ 174 if (m.m_source == PM_PROC_NR && m.m_type == PROC_EVENT) { 175 got_proc_event(&m); 176 177 continue; 178 } 179 180 /* Dispatch the request. */ 181 call_index = (unsigned int)(m.m_type - IPC_BASE); 182 183 if (call_index < __arraycount(call_vec) && 184 call_vec[call_index] != NULL) { 185 r = call_vec[call_index](&m); 186 } else 187 r = ENOSYS; 188 189 /* Send a reply, if needed. */ 190 if (r != SUSPEND) { 191 if (verbose) 192 printf("IPC: call result %d\n", r); 193 194 m.m_type = r; 195 /* 196 * Other fields may have been set by the handler 197 * function already. 198 */ 199 200 if ((r = ipc_sendnb(m.m_source, &m)) != OK) 201 printf("IPC: send error %d\n", r); 202 } 203 204 /* XXX there must be a better way to do this! */ 205 update_refcount_and_destroy(); 206 } 207 208 /* NOTREACHED */ 209 return 0; 210 } 211