]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygserver/bsd_helper.cc
* bsd_helper.h: Throughout, convert "struct thread" to "class thread".
[newlib-cygwin.git] / winsup / cygserver / bsd_helper.cc
CommitLineData
282113ba
CV
1/* bsd_helper.cc
2
681bb2f7 3 Copyright 2003, 2004, 2005, 2007, 2012, 2014 Red Hat Inc.
282113ba
CV
4
5This file is part of Cygwin.
6
7This software is a copyrighted work licensed under the terms of the
8Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9details. */
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 */
38int
39win_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 */
53int
54win_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__)
65static 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__)
74static 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
81CRITICAL_SECTION ipcht_cs;
82
83struct ipc_hookthread_storage {
84 HANDLE process_hdl;
85 proc ipcblk;
86};
87
88struct ipc_hookthread {
96e949de 89 SLIST_ENTRY (ipc_hookthread) sht_next;
282113ba
CV
90 HANDLE thread;
91 DWORD winpid;
92 struct vmspace vmspace;
93};
96e949de 94static SLIST_HEAD (, ipc_hookthread) ipcht_list; /* list of hook threads */
282113ba
CV
95
96static HANDLE ipcexit_event;
97
98struct vmspace *
99ipc_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
116static DWORD WINAPI
96e949de 117ipcexit_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. */
165static void
96e949de 166ipcexit_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. */
182int
96e949de 183ipcexit_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 */
232PSID admininstrator_group_sid;
233
234static void
235init_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
244SECURITY_DESCRIPTOR sec_all_nih_sd;
245SECURITY_ATTRIBUTES sec_all_nih = { sizeof (SECURITY_ATTRIBUTES),
246 &sec_all_nih_sd,
247 FALSE };
248
2d015bd6
CV
249void
250securityinit ()
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. */
258tun_bool_t support_sharedmem = TUN_UNDEF;
259tun_bool_t support_msgqueues = TUN_UNDEF;
260tun_bool_t support_semaphores = TUN_UNDEF;
261
262void
263ipcinit ()
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
279int
280ipcunload ()
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 */
298static bool
299is_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 313void *
282113ba
CV
314get_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 */
337int
338ipcperm (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 */
365int
366suser (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 */
377bool
378adjust_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
433faulty:
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
444vm_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
460vm_object_t
461vm_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
471void
472vm_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
485enum tun_params_type {
486 TUN_NULL,
487 TUN_INT,
488 TUN_BOOL
489};
490
491union tun_value {
492 long ival;
493 tun_bool_t bval;
494};
495
496struct 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
505static void
506default_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
547static 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
596void
597tunable_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
659void
61522196 660tunable_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
676void
677tunable_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__ */
This page took 0.277114 seconds and 5 git commands to generate.