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