xref: /minix/minix/servers/ipc/main.c (revision 08cbf5a0)
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