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