]>
Commit | Line | Data |
---|---|---|
282113ba CV |
1 | /* bsd_helper.cc |
2 | ||
681bb2f7 | 3 | Copyright 2003, 2004, 2005, 2007, 2012, 2014 Red Hat Inc. |
282113ba CV |
4 | |
5 | This file is part of Cygwin. | |
6 | ||
7 | This software is a copyrighted work licensed under the terms of the | |
8 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
9 | details. */ | |
10 | #ifdef __OUTSIDE_CYGWIN__ | |
11 | #include "woutsup.h" | |
1dcd520b | 12 | #include <errno.h> |
282113ba CV |
13 | #define _KERNEL 1 |
14 | #define __BSD_VISIBLE 1 | |
15 | #include <sys/smallprint.h> | |
16 | #include <sys/cygwin.h> | |
17 | #include <sys/ipc.h> | |
18 | #include <sys/param.h> | |
19 | #include <sys/msg.h> | |
20 | #include <sys/queue.h> | |
21 | #include <malloc.h> | |
22 | #include <unistd.h> | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <ctype.h> | |
26 | ||
282113ba CV |
27 | #include "cygserver.h" |
28 | #include "process.h" | |
29 | #include "cygserver_ipc.h" | |
30 | #include "cygserver_msg.h" | |
31 | #include "cygserver_sem.h" | |
32 | #include "cygserver_shm.h" | |
33 | ||
34 | /* | |
35 | * Copy a piece of memory from the client process into the server process. | |
36 | * Returns an error code. | |
37 | */ | |
38 | int | |
39 | win_copyin (struct thread *td, const void *client_src, | |
40 | void *server_tgt, size_t len) | |
41 | { | |
42 | if (!ReadProcessMemory (td->client->handle (), client_src, server_tgt, | |
43 | len, NULL)) | |
44 | return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, | |
45 | GetLastError (), EINVAL); | |
46 | return 0; | |
47 | } | |
48 | ||
49 | /* | |
50 | * Copy a piece of memory from the server process into the client process. | |
51 | * Returns an error code. | |
52 | */ | |
53 | int | |
54 | win_copyout (struct thread *td, const void *server_src, | |
55 | void *client_tgt, size_t len) | |
56 | { | |
57 | if (!WriteProcessMemory (td->client->handle (), client_tgt, server_src, | |
58 | len, NULL)) | |
59 | return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, | |
60 | GetLastError (), EINVAL); | |
61 | return 0; | |
62 | } | |
63 | ||
64 | #define enter_critical_section(c) _enter_critical_section((c),__FILE__,__LINE__) | |
65 | static void | |
66 | _enter_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line) | |
67 | { | |
68 | _log (file, line, LOG_DEBUG, "Try enter critical section(%p)", pcs); | |
69 | EnterCriticalSection (pcs); | |
70 | _log (file, line, LOG_DEBUG, "Entered critical section(%p)", pcs); | |
71 | } | |
72 | ||
73 | #define leave_critical_section(c) _leave_critical_section((c),__FILE__,__LINE__) | |
74 | static void | |
75 | _leave_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line) | |
76 | { | |
77 | LeaveCriticalSection (pcs); | |
78 | _log (file, line, LOG_DEBUG, "Left critical section(%p)", pcs); | |
79 | } | |
80 | ||
81 | CRITICAL_SECTION ipcht_cs; | |
82 | ||
83 | struct ipc_hookthread_storage { | |
84 | HANDLE process_hdl; | |
85 | proc ipcblk; | |
86 | }; | |
87 | ||
88 | struct ipc_hookthread { | |
96e949de | 89 | SLIST_ENTRY (ipc_hookthread) sht_next; |
282113ba CV |
90 | HANDLE thread; |
91 | DWORD winpid; | |
92 | struct vmspace vmspace; | |
93 | }; | |
96e949de | 94 | static SLIST_HEAD (, ipc_hookthread) ipcht_list; /* list of hook threads */ |
282113ba CV |
95 | |
96 | static HANDLE ipcexit_event; | |
97 | ||
98 | struct vmspace * | |
99 | ipc_p_vmspace (struct proc *proc) | |
100 | { | |
101 | struct vmspace *ret = NULL; | |
102 | ipc_hookthread *ipcht_entry; | |
103 | enter_critical_section (&ipcht_cs); | |
104 | SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) | |
105 | { | |
106 | if (ipcht_entry->winpid == proc->winpid) | |
107 | { | |
108 | ret = proc->p_vmspace = &ipcht_entry->vmspace; | |
109 | break; | |
110 | } | |
111 | } | |
112 | leave_critical_section (&ipcht_cs); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | static DWORD WINAPI | |
96e949de | 117 | ipcexit_hookthread (const LPVOID param) |
282113ba CV |
118 | { |
119 | ipc_hookthread_storage *shs = (ipc_hookthread_storage *) param; | |
120 | HANDLE obj[2] = { ipcexit_event, shs->process_hdl }; | |
121 | switch (WaitForMultipleObjects (2, obj, FALSE, INFINITE)) | |
122 | { | |
123 | case WAIT_OBJECT_0: | |
124 | /* Cygserver shutdown. */ | |
125 | /*FALLTHRU*/ | |
126 | case WAIT_OBJECT_0 + 1: | |
127 | /* Process exited. Call semexit_myhook to handle SEM_UNDOs for the | |
128 | exiting process and shmexit_myhook to keep track of shared | |
129 | memory. */ | |
130 | if (Giant.owner == shs->ipcblk.winpid) | |
dafef5e2 | 131 | mtx_unlock (&Giant); |
282113ba CV |
132 | if (support_semaphores == TUN_TRUE) |
133 | semexit_myhook (NULL, &shs->ipcblk); | |
134 | if (support_sharedmem == TUN_TRUE) | |
135 | { | |
136 | _mtx_lock (&Giant, shs->ipcblk.winpid, __FILE__, __LINE__); | |
137 | ipc_p_vmspace (&shs->ipcblk); | |
138 | shmexit_myhook (shs->ipcblk.p_vmspace); | |
139 | mtx_unlock (&Giant); | |
140 | } | |
141 | break; | |
142 | default: | |
143 | /* FIXME: Panic? */ | |
144 | break; | |
145 | } | |
146 | CloseHandle (shs->process_hdl); | |
147 | ipc_hookthread *ipcht_entry, *sav_entry; | |
148 | enter_critical_section (&ipcht_cs); | |
149 | SLIST_FOREACH_SAFE (ipcht_entry, &ipcht_list, sht_next, sav_entry) | |
150 | { | |
151 | if (ipcht_entry->winpid == shs->ipcblk.winpid) | |
152 | { | |
153 | SLIST_REMOVE (&ipcht_list, ipcht_entry, ipc_hookthread, sht_next); | |
dafef5e2 | 154 | CloseHandle (ipcht_entry->thread); |
282113ba CV |
155 | delete ipcht_entry; |
156 | } | |
157 | } | |
158 | leave_critical_section (&ipcht_cs); | |
159 | delete shs; | |
160 | return 0; | |
161 | } | |
162 | ||
163 | /* Deletes all pending hook threads. Called by ipcunload() which in turn | |
164 | is called by the cygserver main routine. */ | |
165 | static void | |
96e949de | 166 | ipcexit_dispose_hookthreads (void) |
282113ba CV |
167 | { |
168 | SetEvent (ipcexit_event); | |
169 | ipc_hookthread *ipcht_entry; | |
170 | enter_critical_section (&ipcht_cs); | |
171 | SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) | |
172 | { | |
173 | WaitForSingleObject (ipcht_entry->thread, 1000); | |
174 | /* Don't bother removing the linked list on cygserver shutdown. */ | |
175 | /* FIXME: Error handling? */ | |
176 | } | |
177 | leave_critical_section (&ipcht_cs); | |
178 | } | |
179 | ||
180 | /* Creates the per process wait thread. Called by semget() under locked | |
181 | Giant mutex condition. */ | |
182 | int | |
96e949de | 183 | ipcexit_creat_hookthread (struct thread *td) |
282113ba CV |
184 | { |
185 | ipc_hookthread *ipcht_entry; | |
186 | int ret = -1; | |
187 | enter_critical_section (&ipcht_cs); | |
188 | SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) | |
189 | { | |
190 | if (ipcht_entry->winpid == td->ipcblk->winpid) | |
191 | ret = 0; | |
192 | } | |
193 | leave_critical_section (&ipcht_cs); | |
194 | if (!ret) | |
195 | return 0; | |
196 | ||
197 | DWORD tid; | |
198 | ipc_hookthread_storage *shs = new ipc_hookthread_storage; | |
199 | if (!DuplicateHandle (GetCurrentProcess (), td->client->handle (), | |
200 | GetCurrentProcess (), &shs->process_hdl, | |
201 | 0, FALSE, DUPLICATE_SAME_ACCESS)) | |
202 | { | |
cf06a0b1 | 203 | delete shs; |
681bb2f7 | 204 | log (LOG_CRIT, "failed to duplicate process handle, error = %u", |
282113ba CV |
205 | GetLastError ()); |
206 | return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, | |
207 | GetLastError (), ENOMEM); | |
208 | } | |
209 | shs->ipcblk = *td->ipcblk; | |
210 | HANDLE thread = CreateThread (NULL, 0, ipcexit_hookthread, shs, 0, &tid); | |
211 | if (!thread) | |
212 | { | |
ca6183c3 | 213 | delete shs; |
681bb2f7 | 214 | log (LOG_CRIT, "failed to create thread, error = %u", GetLastError ()); |
282113ba CV |
215 | return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, |
216 | GetLastError (), ENOMEM); | |
217 | } | |
218 | ipcht_entry = new ipc_hookthread; | |
219 | ipcht_entry->thread = thread; | |
220 | ipcht_entry->winpid = td->ipcblk->winpid; | |
221 | ipcht_entry->vmspace.vm_map = NULL; | |
222 | ipcht_entry->vmspace.vm_shm = NULL; | |
223 | enter_critical_section (&ipcht_cs); | |
224 | SLIST_INSERT_HEAD (&ipcht_list, ipcht_entry, sht_next); | |
225 | leave_critical_section (&ipcht_cs); | |
226 | return 0; | |
227 | } | |
228 | ||
229 | /* | |
230 | * Need the admins group SID to compare with groups in client token. | |
231 | */ | |
232 | PSID admininstrator_group_sid; | |
233 | ||
234 | static void | |
235 | init_admin_sid (void) | |
236 | { | |
7131554a CV |
237 | SID_IDENTIFIER_AUTHORITY nt_auth = {SECURITY_NT_AUTHORITY}; |
238 | if (! AllocateAndInitializeSid (&nt_auth, 2, 32, 544, 0, 0, 0, 0, 0, 0, | |
239 | &admininstrator_group_sid)) | |
681bb2f7 | 240 | panic ("failed to create well known sids, error = %u", |
7131554a | 241 | GetLastError ()); |
282113ba CV |
242 | } |
243 | ||
244 | SECURITY_DESCRIPTOR sec_all_nih_sd; | |
245 | SECURITY_ATTRIBUTES sec_all_nih = { sizeof (SECURITY_ATTRIBUTES), | |
246 | &sec_all_nih_sd, | |
247 | FALSE }; | |
248 | ||
2d015bd6 CV |
249 | void |
250 | securityinit () | |
251 | { | |
252 | InitializeSecurityDescriptor (&sec_all_nih_sd, SECURITY_DESCRIPTOR_REVISION); | |
253 | SetSecurityDescriptorDacl (&sec_all_nih_sd, TRUE, 0, FALSE); | |
254 | init_admin_sid (); | |
255 | } | |
256 | ||
282113ba CV |
257 | /* Global vars, determining whether the IPC stuff should be started or not. */ |
258 | tun_bool_t support_sharedmem = TUN_UNDEF; | |
259 | tun_bool_t support_msgqueues = TUN_UNDEF; | |
260 | tun_bool_t support_semaphores = TUN_UNDEF; | |
261 | ||
262 | void | |
263 | ipcinit () | |
264 | { | |
96e949de | 265 | mtx_init (&Giant, "Giant", NULL, MTX_DEF); |
282113ba CV |
266 | msleep_init (); |
267 | ipcexit_event = CreateEvent (NULL, TRUE, FALSE, NULL); | |
268 | if (!ipcexit_event) | |
269 | panic ("Failed to create ipcexit event object"); | |
270 | InitializeCriticalSection (&ipcht_cs); | |
271 | if (support_msgqueues == TUN_TRUE) | |
272 | msginit (); | |
273 | if (support_semaphores == TUN_TRUE) | |
274 | seminit (); | |
275 | if (support_sharedmem == TUN_TRUE) | |
276 | shminit (); | |
277 | } | |
278 | ||
279 | int | |
280 | ipcunload () | |
281 | { | |
96e949de | 282 | ipcexit_dispose_hookthreads (); |
282113ba CV |
283 | CloseHandle (ipcexit_event); |
284 | wakeup_all (); | |
285 | if (support_semaphores == TUN_TRUE) | |
286 | semunload (); | |
287 | if (support_sharedmem == TUN_TRUE) | |
288 | shmunload (); | |
289 | if (support_msgqueues == TUN_TRUE) | |
96e949de CV |
290 | msgunload (); |
291 | mtx_destroy (&Giant); | |
282113ba CV |
292 | return 0; |
293 | } | |
294 | ||
295 | /* | |
296 | * Helper function to find a gid in a list of gids. | |
297 | */ | |
298 | static bool | |
299 | is_grp_member (gid_t grp, gid_t *grplist, int listsize) | |
300 | { | |
301 | if (grplist) | |
302 | for (; listsize > 0; --listsize) | |
303 | if (grp == grplist[listsize - 1]) | |
304 | return true; | |
305 | return false; | |
306 | } | |
307 | ||
308 | /* | |
309 | * Helper function to get a specific token information from a token. | |
310 | * This function mallocs the necessary buffer spcae by itself. It | |
311 | * must be free'd by the calling function. | |
312 | */ | |
d4db08d7 | 313 | void * |
282113ba CV |
314 | get_token_info (HANDLE tok, TOKEN_INFORMATION_CLASS tic) |
315 | { | |
316 | void *buf; | |
317 | DWORD size; | |
318 | ||
319 | if (!GetTokenInformation (tok, tic, NULL, 0, &size) | |
320 | && GetLastError () != ERROR_INSUFFICIENT_BUFFER) | |
321 | return NULL; | |
322 | if (!(buf = malloc (size))) | |
323 | return NULL; | |
324 | if (!GetTokenInformation (tok, tic, buf, size, &size)) | |
325 | { | |
326 | free (buf); | |
327 | return NULL; | |
328 | } | |
329 | return buf; | |
330 | } | |
331 | ||
332 | /* | |
333 | * Check if client user helds "mode" permission when accessing object | |
334 | * associated with "perm" permission record. | |
335 | * Returns an error code. | |
336 | */ | |
337 | int | |
338 | ipcperm (struct thread *td, ipc_perm *perm, unsigned int mode) | |
339 | { | |
340 | proc *p = td->ipcblk; | |
341 | ||
342 | if (!suser (td)) | |
343 | return 0; | |
344 | if (mode & IPC_M) | |
345 | { | |
346 | return (p->uid != perm->cuid && p->uid != perm->uid) | |
347 | ? EACCES : 0; | |
348 | } | |
349 | if (p->uid != perm->cuid && p->uid != perm->uid) | |
350 | { | |
351 | /* If the user is a member of the creator or owner group, test | |
352 | against group bits, otherwise against other bits. */ | |
353 | mode >>= p->gid != perm->gid && p->gid != perm->cgid | |
354 | && !is_grp_member (perm->gid, p->gidlist, p->gidcnt) | |
355 | && !is_grp_member (perm->cgid, p->gidlist, p->gidcnt) | |
356 | ? 6 : 3; | |
357 | } | |
358 | return (mode & perm->mode) != mode ? EACCES : 0; | |
359 | } | |
360 | ||
361 | /* | |
362 | * Check for client user being superuser. | |
363 | * Returns an error code. | |
364 | */ | |
365 | int | |
366 | suser (struct thread *td) | |
367 | { | |
282113ba CV |
368 | /* This value has been set at ImpersonateNamedPipeClient() time |
369 | using the token information. See adjust_identity_info() below. */ | |
370 | return td->ipcblk->is_admin ? 0 : EACCES; | |
371 | } | |
372 | ||
373 | /* | |
374 | * Retrieves user and group info from impersonated token and creates the | |
375 | * correct uid, gid, gidlist and is_admin entries in p from that. | |
376 | */ | |
377 | bool | |
378 | adjust_identity_info (struct proc *p) | |
379 | { | |
380 | HANDLE tok; | |
381 | ||
282113ba CV |
382 | if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ, TRUE, &tok)) |
383 | { | |
384 | debug ("Failed to open worker thread access token for pid %d, winpid %d", | |
385 | p->cygpid, p->winpid); | |
386 | return false; | |
387 | } | |
388 | ||
389 | /* Get uid from user SID in token. */ | |
390 | PTOKEN_USER user; | |
391 | if (!(user = (PTOKEN_USER)get_token_info (tok, TokenUser))) | |
392 | goto faulty; | |
393 | p->uid = cygwin_internal (CW_GET_UID_FROM_SID, user->User.Sid); | |
394 | free (user); | |
395 | if (p->uid == (uid_t)-1) | |
396 | log (LOG_WARNING, "WARNING: User not found in /etc/passwd! Using uid -1!"); | |
397 | ||
398 | /* Get gid from primary group SID in token. */ | |
399 | PTOKEN_PRIMARY_GROUP pgrp; | |
400 | if (!(pgrp = (PTOKEN_PRIMARY_GROUP)get_token_info (tok, TokenPrimaryGroup))) | |
401 | goto faulty; | |
402 | p->gid = cygwin_internal (CW_GET_GID_FROM_SID, pgrp->PrimaryGroup); | |
403 | free (pgrp); | |
404 | if (p->gid == (gid_t)-1) | |
0f93784f | 405 | log (LOG_WARNING,"WARNING: Group not found in /etc/group! Using gid -1!"); |
282113ba CV |
406 | |
407 | /* Generate gid list from token group's SID list. Also look if the token | |
408 | has an enabled admin group SID. That means, the process has admin | |
409 | privileges. That knowledge is used in suser(). */ | |
410 | PTOKEN_GROUPS gsids; | |
411 | if (!(gsids = (PTOKEN_GROUPS)get_token_info (tok, TokenGroups))) | |
412 | goto faulty; | |
413 | if (gsids->GroupCount) | |
414 | { | |
415 | p->gidlist = (gid_t *) calloc (gsids->GroupCount, sizeof (gid_t)); | |
416 | if (p->gidlist) | |
417 | p->gidcnt = gsids->GroupCount; | |
418 | } | |
419 | for (DWORD i = 0; i < gsids->GroupCount; ++i) | |
420 | { | |
421 | if (p->gidlist) | |
422 | p->gidlist[i] = cygwin_internal (CW_GET_GID_FROM_SID, | |
423 | gsids->Groups[i].Sid); | |
424 | if (EqualSid (gsids->Groups[i].Sid, admininstrator_group_sid) | |
425 | && (gsids->Groups[i].Attributes & SE_GROUP_ENABLED)) | |
426 | p->is_admin = true; | |
427 | } | |
428 | free (gsids); | |
429 | ||
430 | CloseHandle (tok); | |
431 | return true; | |
432 | ||
433 | faulty: | |
434 | CloseHandle (tok); | |
435 | log (LOG_CRIT, "Failed to get token information for pid %d, winpid %d", | |
436 | p->cygpid, p->winpid); | |
437 | return false; | |
438 | } | |
439 | ||
440 | /* | |
441 | * Windows wrapper implementation of the VM functions called by sysv_shm.cc. | |
442 | */ | |
443 | ||
444 | vm_object_t | |
445 | _vm_pager_allocate (int size, int shmflg) | |
446 | { | |
447 | /* Create the file mapping object with full access for everyone. This is | |
448 | necessary to allow later calls to shmctl(..., IPC_SET,...) to | |
449 | change the access rights and ownership of a shared memory region. | |
450 | The access rights are tested at the beginning of every shm... function. | |
451 | Note that this does not influence the actual read or write access | |
452 | defined in a call to shmat. */ | |
453 | vm_object_t object = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih, | |
454 | PAGE_READWRITE, 0, size, NULL); | |
455 | if (!object) | |
681bb2f7 | 456 | panic ("CreateFileMapping in _vm_pager_allocate failed, %u", GetLastError ()); |
282113ba CV |
457 | return object; |
458 | } | |
459 | ||
460 | vm_object_t | |
461 | vm_object_duplicate (struct thread *td, vm_object_t object) | |
462 | { | |
463 | vm_object_t dup_object; | |
96e949de CV |
464 | if (!DuplicateHandle (GetCurrentProcess (), object, |
465 | td->client->handle (), &dup_object, | |
466 | 0, TRUE, DUPLICATE_SAME_ACCESS)) | |
681bb2f7 | 467 | panic ("!DuplicateHandle in vm_object_duplicate failed, %u", GetLastError ()); |
282113ba CV |
468 | return dup_object; |
469 | } | |
470 | ||
471 | void | |
472 | vm_object_deallocate (vm_object_t object) | |
473 | { | |
474 | if (object) | |
475 | CloseHandle (object); | |
476 | } | |
477 | ||
478 | /* | |
479 | * Tunable parameters are read from a system wide cygserver.conf file. | |
480 | * On the first call to tunable_int_fetch, the file is read and the | |
481 | * parameters are set accordingly. Each parameter has default, max and | |
482 | * min settings. | |
483 | */ | |
484 | ||
485 | enum tun_params_type { | |
486 | TUN_NULL, | |
487 | TUN_INT, | |
488 | TUN_BOOL | |
489 | }; | |
490 | ||
491 | union tun_value { | |
492 | long ival; | |
493 | tun_bool_t bval; | |
494 | }; | |
495 | ||
496 | struct tun_struct { | |
497 | const char *name; | |
498 | tun_params_type type; | |
499 | union tun_value value; | |
500 | union tun_value min; | |
501 | union tun_value max; | |
502 | void (*check_func)(tun_struct *, char *, const char *); | |
503 | }; | |
504 | ||
505 | static void | |
506 | default_tun_check (tun_struct *that, char *value, const char *fname) | |
507 | { | |
508 | char *c = NULL; | |
509 | tun_value val; | |
510 | switch (that->type) | |
511 | { | |
512 | case TUN_INT: | |
513 | val.ival = strtoul (value, &c, 10); | |
514 | if (!val.ival || (c && *c)) | |
515 | panic ("Error in config file %s: Value of parameter %s malformed", | |
516 | fname, that->name); | |
517 | if (val.ival < that->min.ival || val.ival > that->max.ival) | |
518 | panic ("Error in config file %s: Value of parameter %s must be " | |
519 | "between %lu and %lu", | |
520 | fname, that->name, that->min.ival, that->max.ival); | |
521 | if (that->value.ival) | |
522 | panic ("Error in config file %s: Parameter %s set twice.\n", | |
523 | fname, that->name); | |
524 | that->value.ival = val.ival; | |
525 | break; | |
526 | case TUN_BOOL: | |
527 | if (!strcasecmp (value, "no") || !strcasecmp (value, "n") | |
528 | || !strcasecmp (value, "false") || !strcasecmp (value, "f") | |
529 | || !strcasecmp (value, "0")) | |
530 | val.bval = TUN_FALSE; | |
531 | else if (!strcasecmp (value, "yes") || !strcasecmp (value, "y") | |
532 | || !strcasecmp (value, "true") || !strcasecmp (value, "t") | |
533 | || !strcasecmp (value, "1")) | |
534 | val.bval = TUN_TRUE; | |
535 | else | |
536 | panic ("Error in config file %s: Value of parameter %s malformed\n" | |
537 | "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname, that->name); | |
538 | that->value.bval = val.bval; | |
539 | break; | |
540 | default: | |
541 | /* Shouldn't happen. */ | |
542 | panic ("Internal error: Wrong type of tunable parameter"); | |
543 | break; | |
544 | } | |
545 | } | |
546 | ||
547 | static tun_struct tunable_params[] = | |
548 | { | |
549 | /* SRV */ | |
2d015bd6 CV |
550 | { "kern.srv.cleanup_threads", TUN_INT, {0}, {1}, {32}, default_tun_check}, |
551 | { "kern.srv.request_threads", TUN_INT, {0}, {1}, {310}, default_tun_check}, | |
552 | { "kern.srv.process_cache_size", TUN_INT, {0}, {1}, {310}, default_tun_check}, | |
282113ba CV |
553 | { "kern.srv.sharedmem", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, |
554 | { "kern.srv.msgqueues", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, | |
555 | { "kern.srv.semaphores", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, | |
556 | ||
557 | /* LOG */ | |
558 | { "kern.log.syslog", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, | |
559 | { "kern.log.stderr", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, | |
560 | { "kern.log.debug", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, | |
561 | { "kern.log.level", TUN_INT, {0}, {1}, {7}, default_tun_check}, | |
562 | ||
563 | /* MSG */ | |
11377ba6 | 564 | { "kern.ipc.msgseg", TUN_INT, {0}, {256}, {65535}, default_tun_check}, |
282113ba | 565 | { "kern.ipc.msgssz", TUN_INT, {0}, {8}, {1024}, default_tun_check}, |
11377ba6 | 566 | { "kern.ipc.msgmnb", TUN_INT, {0}, {1}, {65535}, default_tun_check}, |
282113ba | 567 | { "kern.ipc.msgmni", TUN_INT, {0}, {1}, {1024}, default_tun_check}, |
11377ba6 | 568 | { "kern.ipc.msgtql", TUN_INT, {0}, {1}, {1024}, default_tun_check}, |
282113ba CV |
569 | |
570 | /* SEM */ | |
571 | //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
572 | { "kern.ipc.semmni", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
573 | { "kern.ipc.semmns", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
574 | { "kern.ipc.semmnu", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
575 | { "kern.ipc.semmsl", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
576 | { "kern.ipc.semopm", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
577 | { "kern.ipc.semume", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
578 | //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check}, | |
579 | { "kern.ipc.semvmx", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
580 | { "kern.ipc.semaem", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
581 | ||
582 | /* SHM */ | |
583 | { "kern.ipc.shmmaxpgs", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
584 | //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
585 | { "kern.ipc.shmmni", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
586 | { "kern.ipc.shmseg", TUN_INT, {0}, {1}, {32767}, default_tun_check}, | |
8f14a113 | 587 | { "kern.ipc.shm_allow_removed", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, |
282113ba CV |
588 | //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check}, |
589 | { NULL, TUN_NULL, {0}, {0}, {0}, NULL} | |
590 | }; | |
591 | ||
592 | #define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c) | |
593 | #define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c) | |
594 | #define end_of_content(c) (!*(c) || *(c) == '#') | |
595 | ||
596 | void | |
597 | tunable_param_init (const char *config_file, bool force) | |
598 | { | |
599 | FILE *fp = fopen (config_file, "rt"); | |
600 | if (!fp) | |
601 | { | |
602 | if (force) | |
603 | panic ("can't open config file %s\n", config_file); | |
604 | return; | |
605 | } | |
606 | char line[1024]; | |
607 | while (fgets (line, 1024, fp)) | |
608 | { | |
609 | char *c = strrchr (line, '\n'); | |
610 | if (!c) | |
611 | panic ("Line too long in confg file %s\n", config_file); | |
612 | /* Overwrite trailing NL. */ | |
613 | *c = '\0'; | |
614 | c = line; | |
615 | skip_whitespace (c); | |
616 | if (end_of_content (c)) | |
617 | continue; | |
618 | /* So we are on the first character of a parameter name. */ | |
619 | char *name = c; | |
620 | /* Find end of name. */ | |
621 | skip_nonwhitespace (c); | |
622 | if (end_of_content (c)) | |
623 | { | |
624 | *c++ = '\0'; | |
625 | panic ("Error in config file %s: Parameter %s has no value.\n", | |
626 | config_file, name); | |
627 | } | |
628 | /* Mark end of name. */ | |
629 | *c++ = '\0'; | |
630 | skip_whitespace (c); | |
631 | if (end_of_content (c)) | |
632 | panic ("Error in config file %s: Parameter %s has no value.\n", | |
633 | config_file, name); | |
634 | /* Now we are on the first character of a parameter's value. */ | |
635 | char *value = c; | |
636 | /* This only works for simple parameters. If complex string parameters | |
637 | are added at one point, the scanning routine must be changed here. */ | |
638 | /* Find end of value. */ | |
639 | skip_nonwhitespace (c); | |
640 | /* Mark end of value. */ | |
641 | *c++ = '\0'; | |
642 | /* Now look if name is one from our list. */ | |
643 | tun_struct *s; | |
644 | for (s = &tunable_params[0]; s->name; ++s) | |
645 | if (!strcmp (name, s->name)) | |
646 | { | |
647 | /* Now read value and check for validity. check_func doesn't | |
648 | return on error. */ | |
649 | s->check_func (s, value, config_file); | |
650 | break; | |
651 | } | |
652 | if (!s->name) | |
653 | panic ("Error in config file %s: Unknown parameter %s.\n", | |
654 | config_file, name); | |
655 | } | |
656 | fclose (fp); | |
657 | } | |
658 | ||
659 | void | |
61522196 | 660 | tunable_int_fetch (const char *name, int32_t *tunable_target) |
282113ba CV |
661 | { |
662 | tun_struct *s; | |
663 | for (s = &tunable_params[0]; s->name; ++s) | |
664 | if (!strcmp (name, s->name)) | |
665 | break; | |
666 | if (!s) /* Not found */ | |
667 | return; | |
668 | if (s->type != TUN_INT) /* Wrong type */ | |
669 | return; | |
670 | if (!s->value.ival) /* Not set in config file */ | |
671 | return; | |
672 | *tunable_target = s->value.ival; | |
681bb2f7 | 673 | debug ("\nSet %s to %u\n", name, *tunable_target); |
282113ba CV |
674 | } |
675 | ||
676 | void | |
677 | tunable_bool_fetch (const char *name, tun_bool_t *tunable_target) | |
678 | { | |
679 | tun_struct *s; | |
680 | const char *tun_bool_val_string[] = { "undefined", "no", "yes" }; | |
681 | for (s = &tunable_params[0]; s->name; ++s) | |
682 | if (!strcmp (name, s->name)) | |
683 | break; | |
684 | if (!s) /* Not found */ | |
685 | return; | |
686 | if (s->type != TUN_BOOL) /* Wrong type */ | |
687 | return; | |
688 | if (!s->value.ival) /* Not set in config file */ | |
689 | return; | |
690 | *tunable_target = s->value.bval; | |
691 | debug ("\nSet %s to %s\n", name, tun_bool_val_string[*tunable_target]); | |
692 | } | |
693 | #endif /* __OUTSIDE_CYGWIN__ */ |