]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygserver/bsd_helper.cc
* bsd_helper.cc (ipcexit_creat_hookthread): Delete shs to make
[newlib-cygwin.git] / winsup / cygserver / bsd_helper.cc
1 /* bsd_helper.cc
2
3 Copyright 2003, 2004, 2005, 2007, 2012, 2014 Red Hat Inc.
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"
12 #include <errno.h>
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
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 {
89 SLIST_ENTRY (ipc_hookthread) sht_next;
90 HANDLE thread;
91 DWORD winpid;
92 struct vmspace vmspace;
93 };
94 static SLIST_HEAD (, ipc_hookthread) ipcht_list; /* list of hook threads */
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
117 ipcexit_hookthread (const LPVOID param)
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)
131 mtx_unlock (&Giant);
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);
154 CloseHandle (ipcht_entry->thread);
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
166 ipcexit_dispose_hookthreads (void)
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
183 ipcexit_creat_hookthread (struct thread *td)
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 {
203 log (LOG_CRIT, "failed to duplicate process handle, error = %u",
204 GetLastError ());
205 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
206 GetLastError (), ENOMEM);
207 }
208 shs->ipcblk = *td->ipcblk;
209 HANDLE thread = CreateThread (NULL, 0, ipcexit_hookthread, shs, 0, &tid);
210 if (!thread)
211 {
212 delete shs;
213 log (LOG_CRIT, "failed to create thread, error = %u", GetLastError ());
214 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
215 GetLastError (), ENOMEM);
216 }
217 ipcht_entry = new ipc_hookthread;
218 ipcht_entry->thread = thread;
219 ipcht_entry->winpid = td->ipcblk->winpid;
220 ipcht_entry->vmspace.vm_map = NULL;
221 ipcht_entry->vmspace.vm_shm = NULL;
222 enter_critical_section (&ipcht_cs);
223 SLIST_INSERT_HEAD (&ipcht_list, ipcht_entry, sht_next);
224 leave_critical_section (&ipcht_cs);
225 return 0;
226 }
227
228 /*
229 * Need the admins group SID to compare with groups in client token.
230 */
231 PSID admininstrator_group_sid;
232
233 static void
234 init_admin_sid (void)
235 {
236 SID_IDENTIFIER_AUTHORITY nt_auth = {SECURITY_NT_AUTHORITY};
237 if (! AllocateAndInitializeSid (&nt_auth, 2, 32, 544, 0, 0, 0, 0, 0, 0,
238 &admininstrator_group_sid))
239 panic ("failed to create well known sids, error = %u",
240 GetLastError ());
241 }
242
243 SECURITY_DESCRIPTOR sec_all_nih_sd;
244 SECURITY_ATTRIBUTES sec_all_nih = { sizeof (SECURITY_ATTRIBUTES),
245 &sec_all_nih_sd,
246 FALSE };
247
248 void
249 securityinit ()
250 {
251 InitializeSecurityDescriptor (&sec_all_nih_sd, SECURITY_DESCRIPTOR_REVISION);
252 SetSecurityDescriptorDacl (&sec_all_nih_sd, TRUE, 0, FALSE);
253 init_admin_sid ();
254 }
255
256 /* Global vars, determining whether the IPC stuff should be started or not. */
257 tun_bool_t support_sharedmem = TUN_UNDEF;
258 tun_bool_t support_msgqueues = TUN_UNDEF;
259 tun_bool_t support_semaphores = TUN_UNDEF;
260
261 void
262 ipcinit ()
263 {
264 mtx_init (&Giant, "Giant", NULL, MTX_DEF);
265 msleep_init ();
266 ipcexit_event = CreateEvent (NULL, TRUE, FALSE, NULL);
267 if (!ipcexit_event)
268 panic ("Failed to create ipcexit event object");
269 InitializeCriticalSection (&ipcht_cs);
270 if (support_msgqueues == TUN_TRUE)
271 msginit ();
272 if (support_semaphores == TUN_TRUE)
273 seminit ();
274 if (support_sharedmem == TUN_TRUE)
275 shminit ();
276 }
277
278 int
279 ipcunload ()
280 {
281 ipcexit_dispose_hookthreads ();
282 CloseHandle (ipcexit_event);
283 wakeup_all ();
284 if (support_semaphores == TUN_TRUE)
285 semunload ();
286 if (support_sharedmem == TUN_TRUE)
287 shmunload ();
288 if (support_msgqueues == TUN_TRUE)
289 msgunload ();
290 mtx_destroy (&Giant);
291 return 0;
292 }
293
294 /*
295 * Helper function to find a gid in a list of gids.
296 */
297 static bool
298 is_grp_member (gid_t grp, gid_t *grplist, int listsize)
299 {
300 if (grplist)
301 for (; listsize > 0; --listsize)
302 if (grp == grplist[listsize - 1])
303 return true;
304 return false;
305 }
306
307 /*
308 * Helper function to get a specific token information from a token.
309 * This function mallocs the necessary buffer spcae by itself. It
310 * must be free'd by the calling function.
311 */
312 void *
313 get_token_info (HANDLE tok, TOKEN_INFORMATION_CLASS tic)
314 {
315 void *buf;
316 DWORD size;
317
318 if (!GetTokenInformation (tok, tic, NULL, 0, &size)
319 && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
320 return NULL;
321 if (!(buf = malloc (size)))
322 return NULL;
323 if (!GetTokenInformation (tok, tic, buf, size, &size))
324 {
325 free (buf);
326 return NULL;
327 }
328 return buf;
329 }
330
331 /*
332 * Check if client user helds "mode" permission when accessing object
333 * associated with "perm" permission record.
334 * Returns an error code.
335 */
336 int
337 ipcperm (struct thread *td, ipc_perm *perm, unsigned int mode)
338 {
339 proc *p = td->ipcblk;
340
341 if (!suser (td))
342 return 0;
343 if (mode & IPC_M)
344 {
345 return (p->uid != perm->cuid && p->uid != perm->uid)
346 ? EACCES : 0;
347 }
348 if (p->uid != perm->cuid && p->uid != perm->uid)
349 {
350 /* If the user is a member of the creator or owner group, test
351 against group bits, otherwise against other bits. */
352 mode >>= p->gid != perm->gid && p->gid != perm->cgid
353 && !is_grp_member (perm->gid, p->gidlist, p->gidcnt)
354 && !is_grp_member (perm->cgid, p->gidlist, p->gidcnt)
355 ? 6 : 3;
356 }
357 return (mode & perm->mode) != mode ? EACCES : 0;
358 }
359
360 /*
361 * Check for client user being superuser.
362 * Returns an error code.
363 */
364 int
365 suser (struct thread *td)
366 {
367 /* This value has been set at ImpersonateNamedPipeClient() time
368 using the token information. See adjust_identity_info() below. */
369 return td->ipcblk->is_admin ? 0 : EACCES;
370 }
371
372 /*
373 * Retrieves user and group info from impersonated token and creates the
374 * correct uid, gid, gidlist and is_admin entries in p from that.
375 */
376 bool
377 adjust_identity_info (struct proc *p)
378 {
379 HANDLE tok;
380
381 if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ, TRUE, &tok))
382 {
383 debug ("Failed to open worker thread access token for pid %d, winpid %d",
384 p->cygpid, p->winpid);
385 return false;
386 }
387
388 /* Get uid from user SID in token. */
389 PTOKEN_USER user;
390 if (!(user = (PTOKEN_USER)get_token_info (tok, TokenUser)))
391 goto faulty;
392 p->uid = cygwin_internal (CW_GET_UID_FROM_SID, user->User.Sid);
393 free (user);
394 if (p->uid == (uid_t)-1)
395 log (LOG_WARNING, "WARNING: User not found in /etc/passwd! Using uid -1!");
396
397 /* Get gid from primary group SID in token. */
398 PTOKEN_PRIMARY_GROUP pgrp;
399 if (!(pgrp = (PTOKEN_PRIMARY_GROUP)get_token_info (tok, TokenPrimaryGroup)))
400 goto faulty;
401 p->gid = cygwin_internal (CW_GET_GID_FROM_SID, pgrp->PrimaryGroup);
402 free (pgrp);
403 if (p->gid == (gid_t)-1)
404 log (LOG_WARNING,"WARNING: Group not found in /etc/group! Using gid -1!");
405
406 /* Generate gid list from token group's SID list. Also look if the token
407 has an enabled admin group SID. That means, the process has admin
408 privileges. That knowledge is used in suser(). */
409 PTOKEN_GROUPS gsids;
410 if (!(gsids = (PTOKEN_GROUPS)get_token_info (tok, TokenGroups)))
411 goto faulty;
412 if (gsids->GroupCount)
413 {
414 p->gidlist = (gid_t *) calloc (gsids->GroupCount, sizeof (gid_t));
415 if (p->gidlist)
416 p->gidcnt = gsids->GroupCount;
417 }
418 for (DWORD i = 0; i < gsids->GroupCount; ++i)
419 {
420 if (p->gidlist)
421 p->gidlist[i] = cygwin_internal (CW_GET_GID_FROM_SID,
422 gsids->Groups[i].Sid);
423 if (EqualSid (gsids->Groups[i].Sid, admininstrator_group_sid)
424 && (gsids->Groups[i].Attributes & SE_GROUP_ENABLED))
425 p->is_admin = true;
426 }
427 free (gsids);
428
429 CloseHandle (tok);
430 return true;
431
432 faulty:
433 CloseHandle (tok);
434 log (LOG_CRIT, "Failed to get token information for pid %d, winpid %d",
435 p->cygpid, p->winpid);
436 return false;
437 }
438
439 /*
440 * Windows wrapper implementation of the VM functions called by sysv_shm.cc.
441 */
442
443 vm_object_t
444 _vm_pager_allocate (int size, int shmflg)
445 {
446 /* Create the file mapping object with full access for everyone. This is
447 necessary to allow later calls to shmctl(..., IPC_SET,...) to
448 change the access rights and ownership of a shared memory region.
449 The access rights are tested at the beginning of every shm... function.
450 Note that this does not influence the actual read or write access
451 defined in a call to shmat. */
452 vm_object_t object = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih,
453 PAGE_READWRITE, 0, size, NULL);
454 if (!object)
455 panic ("CreateFileMapping in _vm_pager_allocate failed, %u", GetLastError ());
456 return object;
457 }
458
459 vm_object_t
460 vm_object_duplicate (struct thread *td, vm_object_t object)
461 {
462 vm_object_t dup_object;
463 if (!DuplicateHandle (GetCurrentProcess (), object,
464 td->client->handle (), &dup_object,
465 0, TRUE, DUPLICATE_SAME_ACCESS))
466 panic ("!DuplicateHandle in vm_object_duplicate failed, %u", GetLastError ());
467 return dup_object;
468 }
469
470 void
471 vm_object_deallocate (vm_object_t object)
472 {
473 if (object)
474 CloseHandle (object);
475 }
476
477 /*
478 * Tunable parameters are read from a system wide cygserver.conf file.
479 * On the first call to tunable_int_fetch, the file is read and the
480 * parameters are set accordingly. Each parameter has default, max and
481 * min settings.
482 */
483
484 enum tun_params_type {
485 TUN_NULL,
486 TUN_INT,
487 TUN_BOOL
488 };
489
490 union tun_value {
491 long ival;
492 tun_bool_t bval;
493 };
494
495 struct tun_struct {
496 const char *name;
497 tun_params_type type;
498 union tun_value value;
499 union tun_value min;
500 union tun_value max;
501 void (*check_func)(tun_struct *, char *, const char *);
502 };
503
504 static void
505 default_tun_check (tun_struct *that, char *value, const char *fname)
506 {
507 char *c = NULL;
508 tun_value val;
509 switch (that->type)
510 {
511 case TUN_INT:
512 val.ival = strtoul (value, &c, 10);
513 if (!val.ival || (c && *c))
514 panic ("Error in config file %s: Value of parameter %s malformed",
515 fname, that->name);
516 if (val.ival < that->min.ival || val.ival > that->max.ival)
517 panic ("Error in config file %s: Value of parameter %s must be "
518 "between %lu and %lu",
519 fname, that->name, that->min.ival, that->max.ival);
520 if (that->value.ival)
521 panic ("Error in config file %s: Parameter %s set twice.\n",
522 fname, that->name);
523 that->value.ival = val.ival;
524 break;
525 case TUN_BOOL:
526 if (!strcasecmp (value, "no") || !strcasecmp (value, "n")
527 || !strcasecmp (value, "false") || !strcasecmp (value, "f")
528 || !strcasecmp (value, "0"))
529 val.bval = TUN_FALSE;
530 else if (!strcasecmp (value, "yes") || !strcasecmp (value, "y")
531 || !strcasecmp (value, "true") || !strcasecmp (value, "t")
532 || !strcasecmp (value, "1"))
533 val.bval = TUN_TRUE;
534 else
535 panic ("Error in config file %s: Value of parameter %s malformed\n"
536 "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname, that->name);
537 that->value.bval = val.bval;
538 break;
539 default:
540 /* Shouldn't happen. */
541 panic ("Internal error: Wrong type of tunable parameter");
542 break;
543 }
544 }
545
546 static tun_struct tunable_params[] =
547 {
548 /* SRV */
549 { "kern.srv.cleanup_threads", TUN_INT, {0}, {1}, {32}, default_tun_check},
550 { "kern.srv.request_threads", TUN_INT, {0}, {1}, {310}, default_tun_check},
551 { "kern.srv.process_cache_size", TUN_INT, {0}, {1}, {310}, default_tun_check},
552 { "kern.srv.sharedmem", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
553 { "kern.srv.msgqueues", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
554 { "kern.srv.semaphores", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
555
556 /* LOG */
557 { "kern.log.syslog", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
558 { "kern.log.stderr", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
559 { "kern.log.debug", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
560 { "kern.log.level", TUN_INT, {0}, {1}, {7}, default_tun_check},
561
562 /* MSG */
563 { "kern.ipc.msgseg", TUN_INT, {0}, {256}, {65535}, default_tun_check},
564 { "kern.ipc.msgssz", TUN_INT, {0}, {8}, {1024}, default_tun_check},
565 { "kern.ipc.msgmnb", TUN_INT, {0}, {1}, {65535}, default_tun_check},
566 { "kern.ipc.msgmni", TUN_INT, {0}, {1}, {1024}, default_tun_check},
567 { "kern.ipc.msgtql", TUN_INT, {0}, {1}, {1024}, default_tun_check},
568
569 /* SEM */
570 //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check},
571 { "kern.ipc.semmni", TUN_INT, {0}, {1}, {1024}, default_tun_check},
572 { "kern.ipc.semmns", TUN_INT, {0}, {1}, {1024}, default_tun_check},
573 { "kern.ipc.semmnu", TUN_INT, {0}, {1}, {1024}, default_tun_check},
574 { "kern.ipc.semmsl", TUN_INT, {0}, {1}, {1024}, default_tun_check},
575 { "kern.ipc.semopm", TUN_INT, {0}, {1}, {1024}, default_tun_check},
576 { "kern.ipc.semume", TUN_INT, {0}, {1}, {1024}, default_tun_check},
577 //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check},
578 { "kern.ipc.semvmx", TUN_INT, {0}, {1}, {32767}, default_tun_check},
579 { "kern.ipc.semaem", TUN_INT, {0}, {1}, {32767}, default_tun_check},
580
581 /* SHM */
582 { "kern.ipc.shmmaxpgs", TUN_INT, {0}, {1}, {32767}, default_tun_check},
583 //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check},
584 { "kern.ipc.shmmni", TUN_INT, {0}, {1}, {32767}, default_tun_check},
585 { "kern.ipc.shmseg", TUN_INT, {0}, {1}, {32767}, default_tun_check},
586 { "kern.ipc.shm_allow_removed", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
587 //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check},
588 { NULL, TUN_NULL, {0}, {0}, {0}, NULL}
589 };
590
591 #define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c)
592 #define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c)
593 #define end_of_content(c) (!*(c) || *(c) == '#')
594
595 void
596 tunable_param_init (const char *config_file, bool force)
597 {
598 FILE *fp = fopen (config_file, "rt");
599 if (!fp)
600 {
601 if (force)
602 panic ("can't open config file %s\n", config_file);
603 return;
604 }
605 char line[1024];
606 while (fgets (line, 1024, fp))
607 {
608 char *c = strrchr (line, '\n');
609 if (!c)
610 panic ("Line too long in confg file %s\n", config_file);
611 /* Overwrite trailing NL. */
612 *c = '\0';
613 c = line;
614 skip_whitespace (c);
615 if (end_of_content (c))
616 continue;
617 /* So we are on the first character of a parameter name. */
618 char *name = c;
619 /* Find end of name. */
620 skip_nonwhitespace (c);
621 if (end_of_content (c))
622 {
623 *c++ = '\0';
624 panic ("Error in config file %s: Parameter %s has no value.\n",
625 config_file, name);
626 }
627 /* Mark end of name. */
628 *c++ = '\0';
629 skip_whitespace (c);
630 if (end_of_content (c))
631 panic ("Error in config file %s: Parameter %s has no value.\n",
632 config_file, name);
633 /* Now we are on the first character of a parameter's value. */
634 char *value = c;
635 /* This only works for simple parameters. If complex string parameters
636 are added at one point, the scanning routine must be changed here. */
637 /* Find end of value. */
638 skip_nonwhitespace (c);
639 /* Mark end of value. */
640 *c++ = '\0';
641 /* Now look if name is one from our list. */
642 tun_struct *s;
643 for (s = &tunable_params[0]; s->name; ++s)
644 if (!strcmp (name, s->name))
645 {
646 /* Now read value and check for validity. check_func doesn't
647 return on error. */
648 s->check_func (s, value, config_file);
649 break;
650 }
651 if (!s->name)
652 panic ("Error in config file %s: Unknown parameter %s.\n",
653 config_file, name);
654 }
655 fclose (fp);
656 }
657
658 void
659 tunable_int_fetch (const char *name, int32_t *tunable_target)
660 {
661 tun_struct *s;
662 for (s = &tunable_params[0]; s->name; ++s)
663 if (!strcmp (name, s->name))
664 break;
665 if (!s) /* Not found */
666 return;
667 if (s->type != TUN_INT) /* Wrong type */
668 return;
669 if (!s->value.ival) /* Not set in config file */
670 return;
671 *tunable_target = s->value.ival;
672 debug ("\nSet %s to %u\n", name, *tunable_target);
673 }
674
675 void
676 tunable_bool_fetch (const char *name, tun_bool_t *tunable_target)
677 {
678 tun_struct *s;
679 const char *tun_bool_val_string[] = { "undefined", "no", "yes" };
680 for (s = &tunable_params[0]; s->name; ++s)
681 if (!strcmp (name, s->name))
682 break;
683 if (!s) /* Not found */
684 return;
685 if (s->type != TUN_BOOL) /* Wrong type */
686 return;
687 if (!s->value.ival) /* Not set in config file */
688 return;
689 *tunable_target = s->value.bval;
690 debug ("\nSet %s to %s\n", name, tun_bool_val_string[*tunable_target]);
691 }
692 #endif /* __OUTSIDE_CYGWIN__ */
This page took 0.067043 seconds and 5 git commands to generate.