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