diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2003-11-19 18:49:41 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2003-11-19 18:49:41 +0000 |
commit | 282113ba894449ed17e85b296cf0760d5206ac8d (patch) | |
tree | 830bd7ad49e085ea8cde78fea68848fbbca09880 /winsup/cygserver/bsd_helper.cc | |
parent | 64cfc6f213541f0e9e8e57011af8a56aca8c8216 (diff) | |
download | cygnal-282113ba894449ed17e85b296cf0760d5206ac8d.tar.gz cygnal-282113ba894449ed17e85b296cf0760d5206ac8d.tar.bz2 cygnal-282113ba894449ed17e85b296cf0760d5206ac8d.zip |
Don't use safe_new but new throughout. Fix copyright dates
throughout.
* Makefile.in: Accomodate all new files and name changes.
Add a *.d dependency.
(sbindir): Add.
(etcdir): Drop in favor of more appropriate sysconfdir definition.
(sysconfdir): Add.
(CXXFLAGS): Add -MMD flag. Add SYSCONFDIR definition.
(.SUFFIXES): Add.
(install): Add action items.
(libclean): New target.
(fullclean): Ditto.
* bsd_helper.cc: New file.
* bsd_helper.h: Ditto.
* bsd_log.cc: Ditto.
* bsd_log.h: Ditto.
* bsd_mutex.cc: Ditto.
* bsd_mutex.h: Ditto.
* client.cc: Rearrange to build as less as possible if
__INSIDE_CYGWIN__.
(client_request::handle_request): Add Message Queue and Semaphore
handling.
* cygserver.cc: Rearrange to build as less as possible if
__INSIDE_CYGWIN__. Use new debug/log/panic logging functions.
(DEF_CONFIG_FILE): New definition for configuration file. Use
throughout.
(getfunc): Remove.
(__cygserver__printf): Remove.
(client_request_attach_tty::serve): Return error if impersonation
fails.
(print_usage): Pump up help message.
(print_version): Add output of default configuration file.
(main): Accommodate new options. Allow overwrite of threading options
from config file. Call several new initialization functions. Drop
printing dots. Don't define SIGHANDLE inline.
* cygserver.conf: New file.
* cygserver_process.h: Rename to process.h.
* cygserver_transport.h: Rename to transport.h.
* cygserver_transport_pipes.h: Rename to transport_pipes.h.
* cygserver_transport_sockets.h: Rename to transport_sockets.h.
* msg.cc: Rewrite.
* sem.cc: Rewrite.
* shm.cc: Rewrite.
* sysv_msg.cc: New file, derived from FreeBSD version 1.52.
* sysv_sem.cc: New file, derived from FreeBSD version 1.66.
* sysv_shm.cc: New file, derived from FreeBSD version 1.89.
* threaded_queue.cc: Rearrange to build as less as possible if
__INSIDE_CYGWIN__.
* transport.cc (transport_layer_base::impersonate_client): Define bool.
(transport_layer_base::revert_to_self): Ditto.
* transport.h (transport_layer_base::impersonate_client): Declare bool.
(transport_layer_base::revert_to_self): Ditto.
* transport_pipes.cc (transport_layer_pipes::transport_layer_pipes):
Don't call init_security.
(init_security): Remove.
(transport_layer_pipes::accept): Use global sec_all_nih.
(transport_layer_pipes::connect): Ditto.
(transport_layer_pipes::impersonate_client): Define bool.
(transport_layer_pipes::revert_to_self): Ditt.
* transport_pipes.h (transport_layer_pipes::impersonate_client): Declare
bool.
(transport_layer_pipes::revert_to_self): Ditto.
* woutsup.h: Include bsd compatibility headers.
(SIGHANDLE): Add definition.
(__cygserver__printf): Remove definition.
(__noop_printf): Ditto.
(debug_printf): Define using debug.
(syscall_printf): Define using log.
(system_printf): Ditto.
Drop all other _printf definitions.
Diffstat (limited to 'winsup/cygserver/bsd_helper.cc')
-rw-r--r-- | winsup/cygserver/bsd_helper.cc | 694 |
1 files changed, 694 insertions, 0 deletions
diff --git a/winsup/cygserver/bsd_helper.cc b/winsup/cygserver/bsd_helper.cc new file mode 100644 index 000000000..42afd548f --- /dev/null +++ b/winsup/cygserver/bsd_helper.cc @@ -0,0 +1,694 @@ +/* bsd_helper.cc + + Copyright 2003 Red Hat Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#include "cygerrno.h" +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include <sys/smallprint.h> +#include <sys/cygwin.h> +#include <sys/ipc.h> +#include <sys/param.h> +#include <sys/msg.h> +#include <sys/queue.h> +#include <malloc.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#include "security.h" +#include "cygserver.h" +#include "process.h" +#include "cygserver_ipc.h" +#include "cygserver_msg.h" +#include "cygserver_sem.h" +#include "cygserver_shm.h" + +/* + * Copy a piece of memory from the client process into the server process. + * Returns an error code. + */ +int +win_copyin (struct thread *td, const void *client_src, + void *server_tgt, size_t len) +{ + if (!ReadProcessMemory (td->client->handle (), client_src, server_tgt, + len, NULL)) + return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, + GetLastError (), EINVAL); + return 0; +} + +/* + * Copy a piece of memory from the server process into the client process. + * Returns an error code. + */ +int +win_copyout (struct thread *td, const void *server_src, + void *client_tgt, size_t len) +{ + if (!WriteProcessMemory (td->client->handle (), client_tgt, server_src, + len, NULL)) + return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, + GetLastError (), EINVAL); + return 0; +} + +#define enter_critical_section(c) _enter_critical_section((c),__FILE__,__LINE__) +static void +_enter_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line) +{ + _log (file, line, LOG_DEBUG, "Try enter critical section(%p)", pcs); + EnterCriticalSection (pcs); + _log (file, line, LOG_DEBUG, "Entered critical section(%p)", pcs); +} + +#define leave_critical_section(c) _leave_critical_section((c),__FILE__,__LINE__) +static void +_leave_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line) +{ + LeaveCriticalSection (pcs); + _log (file, line, LOG_DEBUG, "Left critical section(%p)", pcs); +} + +CRITICAL_SECTION ipcht_cs; + +struct ipc_hookthread_storage { + HANDLE process_hdl; + proc ipcblk; +}; + +struct ipc_hookthread { + SLIST_ENTRY(ipc_hookthread) sht_next; + HANDLE thread; + DWORD winpid; + struct vmspace vmspace; +}; +static SLIST_HEAD(, ipc_hookthread) ipcht_list; /* list of hook threads */ + +static HANDLE ipcexit_event; + +struct vmspace * +ipc_p_vmspace (struct proc *proc) +{ + struct vmspace *ret = NULL; + ipc_hookthread *ipcht_entry; + enter_critical_section (&ipcht_cs); + SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) + { + if (ipcht_entry->winpid == proc->winpid) + { + ret = proc->p_vmspace = &ipcht_entry->vmspace; + break; + } + } + leave_critical_section (&ipcht_cs); + return ret; +} + +static DWORD WINAPI +ipcexit_hookthread(const LPVOID param) +{ + ipc_hookthread_storage *shs = (ipc_hookthread_storage *) param; + HANDLE obj[2] = { ipcexit_event, shs->process_hdl }; + switch (WaitForMultipleObjects (2, obj, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + /* Cygserver shutdown. */ + /*FALLTHRU*/ + case WAIT_OBJECT_0 + 1: + /* Process exited. Call semexit_myhook to handle SEM_UNDOs for the + exiting process and shmexit_myhook to keep track of shared + memory. */ + if (Giant.owner == shs->ipcblk.winpid) + mtx_unlock (&Giant); + if (support_semaphores == TUN_TRUE) + semexit_myhook (NULL, &shs->ipcblk); + if (support_sharedmem == TUN_TRUE) + { + _mtx_lock (&Giant, shs->ipcblk.winpid, __FILE__, __LINE__); + ipc_p_vmspace (&shs->ipcblk); + shmexit_myhook (shs->ipcblk.p_vmspace); + mtx_unlock (&Giant); + } + break; + default: + /* FIXME: Panic? */ + break; + } + CloseHandle (shs->process_hdl); + ipc_hookthread *ipcht_entry, *sav_entry; + enter_critical_section (&ipcht_cs); + SLIST_FOREACH_SAFE (ipcht_entry, &ipcht_list, sht_next, sav_entry) + { + if (ipcht_entry->winpid == shs->ipcblk.winpid) + { + SLIST_REMOVE (&ipcht_list, ipcht_entry, ipc_hookthread, sht_next); + delete ipcht_entry; + } + } + leave_critical_section (&ipcht_cs); + delete shs; + return 0; +} + +/* Deletes all pending hook threads. Called by ipcunload() which in turn + is called by the cygserver main routine. */ +static void +ipcexit_dispose_hookthreads(void) +{ + SetEvent (ipcexit_event); + ipc_hookthread *ipcht_entry; + enter_critical_section (&ipcht_cs); + SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) + { + WaitForSingleObject (ipcht_entry->thread, 1000); + /* Don't bother removing the linked list on cygserver shutdown. */ + /* FIXME: Error handling? */ + } + leave_critical_section (&ipcht_cs); +} + +/* Creates the per process wait thread. Called by semget() under locked + Giant mutex condition. */ +int +ipcexit_creat_hookthread(struct thread *td) +{ + ipc_hookthread *ipcht_entry; + int ret = -1; + enter_critical_section (&ipcht_cs); + SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next) + { + if (ipcht_entry->winpid == td->ipcblk->winpid) + ret = 0; + } + leave_critical_section (&ipcht_cs); + if (!ret) + return 0; + + DWORD tid; + ipc_hookthread_storage *shs = new ipc_hookthread_storage; + if (!DuplicateHandle (GetCurrentProcess (), td->client->handle (), + GetCurrentProcess (), &shs->process_hdl, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + log (LOG_CRIT, "failed to duplicate process handle, error = %lu", + GetLastError ()); + return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, + GetLastError (), ENOMEM); + } + shs->ipcblk = *td->ipcblk; + HANDLE thread = CreateThread (NULL, 0, ipcexit_hookthread, shs, 0, &tid); + if (!thread) + { + log (LOG_CRIT, "failed to create thread, error = %lu", GetLastError ()); + return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR, + GetLastError (), ENOMEM); + } + ipcht_entry = new ipc_hookthread; + ipcht_entry->thread = thread; + ipcht_entry->winpid = td->ipcblk->winpid; + ipcht_entry->vmspace.vm_map = NULL; + ipcht_entry->vmspace.vm_shm = NULL; + enter_critical_section (&ipcht_cs); + SLIST_INSERT_HEAD (&ipcht_list, ipcht_entry, sht_next); + leave_critical_section (&ipcht_cs); + return 0; +} + +/* + * Need the admins group SID to compare with groups in client token. + */ +PSID admininstrator_group_sid; + +static void +init_admin_sid (void) +{ + if (wincap.has_security ()) + { + SID_IDENTIFIER_AUTHORITY nt_auth = {SECURITY_NT_AUTHORITY}; + if (! AllocateAndInitializeSid (&nt_auth, 2, 32, 544, 0, 0, 0, 0, 0, 0, + &admininstrator_group_sid)) + panic ("failed to create well known sids, error = %lu", + GetLastError ()); + } +} + +SECURITY_DESCRIPTOR sec_all_nih_sd; +SECURITY_ATTRIBUTES sec_all_nih = { sizeof (SECURITY_ATTRIBUTES), + &sec_all_nih_sd, + FALSE }; + +/* Global vars, determining whether the IPC stuff should be started or not. */ +tun_bool_t support_sharedmem = TUN_UNDEF; +tun_bool_t support_msgqueues = TUN_UNDEF; +tun_bool_t support_semaphores = TUN_UNDEF; + +void +ipcinit () +{ + InitializeSecurityDescriptor (&sec_all_nih_sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl (&sec_all_nih_sd, TRUE, 0, FALSE); + + init_admin_sid (); + mtx_init(&Giant, "Giant", NULL, MTX_DEF); + msleep_init (); + ipcexit_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (!ipcexit_event) + panic ("Failed to create ipcexit event object"); + InitializeCriticalSection (&ipcht_cs); + if (support_msgqueues == TUN_TRUE) + msginit (); + if (support_semaphores == TUN_TRUE) + seminit (); + if (support_sharedmem == TUN_TRUE) + shminit (); +} + +int +ipcunload () +{ + ipcexit_dispose_hookthreads(); + CloseHandle (ipcexit_event); + wakeup_all (); + if (support_semaphores == TUN_TRUE) + semunload (); + if (support_sharedmem == TUN_TRUE) + shmunload (); + if (support_msgqueues == TUN_TRUE) + msgunload(); + mtx_destroy(&Giant); + return 0; +} + +/* + * Helper function to find a gid in a list of gids. + */ +static bool +is_grp_member (gid_t grp, gid_t *grplist, int listsize) +{ + if (grplist) + for (; listsize > 0; --listsize) + if (grp == grplist[listsize - 1]) + return true; + return false; +} + +/* + * Helper function to get a specific token information from a token. + * This function mallocs the necessary buffer spcae by itself. It + * must be free'd by the calling function. + */ +static void * +get_token_info (HANDLE tok, TOKEN_INFORMATION_CLASS tic) +{ + void *buf; + DWORD size; + + if (!GetTokenInformation (tok, tic, NULL, 0, &size) + && GetLastError () != ERROR_INSUFFICIENT_BUFFER) + return NULL; + if (!(buf = malloc (size))) + return NULL; + if (!GetTokenInformation (tok, tic, buf, size, &size)) + { + free (buf); + return NULL; + } + return buf; +} + +/* + * Check if client user helds "mode" permission when accessing object + * associated with "perm" permission record. + * Returns an error code. + */ +int +ipcperm (struct thread *td, ipc_perm *perm, unsigned int mode) +{ + proc *p = td->ipcblk; + + if (!suser (td)) + return 0; + if (mode & IPC_M) + { + return (p->uid != perm->cuid && p->uid != perm->uid) + ? EACCES : 0; + } + if (p->uid != perm->cuid && p->uid != perm->uid) + { + /* If the user is a member of the creator or owner group, test + against group bits, otherwise against other bits. */ + mode >>= p->gid != perm->gid && p->gid != perm->cgid + && !is_grp_member (perm->gid, p->gidlist, p->gidcnt) + && !is_grp_member (perm->cgid, p->gidlist, p->gidcnt) + ? 6 : 3; + } + return (mode & perm->mode) != mode ? EACCES : 0; +} + +/* + * Check for client user being superuser. + * Returns an error code. + */ +int +suser (struct thread *td) +{ + /* Always superuser on 9x. */ + if (!wincap.has_security ()) + return 0; + + /* This value has been set at ImpersonateNamedPipeClient() time + using the token information. See adjust_identity_info() below. */ + return td->ipcblk->is_admin ? 0 : EACCES; +} + +/* + * Retrieves user and group info from impersonated token and creates the + * correct uid, gid, gidlist and is_admin entries in p from that. + */ +bool +adjust_identity_info (struct proc *p) +{ + HANDLE tok; + + /* No access tokens on 9x. */ + if (!wincap.has_security ()) + return true; + + if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ, TRUE, &tok)) + { + debug ("Failed to open worker thread access token for pid %d, winpid %d", + p->cygpid, p->winpid); + return false; + } + + /* Get uid from user SID in token. */ + PTOKEN_USER user; + if (!(user = (PTOKEN_USER)get_token_info (tok, TokenUser))) + goto faulty; + p->uid = cygwin_internal (CW_GET_UID_FROM_SID, user->User.Sid); + free (user); + if (p->uid == (uid_t)-1) + log (LOG_WARNING, "WARNING: User not found in /etc/passwd! Using uid -1!"); + + /* Get gid from primary group SID in token. */ + PTOKEN_PRIMARY_GROUP pgrp; + if (!(pgrp = (PTOKEN_PRIMARY_GROUP)get_token_info (tok, TokenPrimaryGroup))) + goto faulty; + p->gid = cygwin_internal (CW_GET_GID_FROM_SID, pgrp->PrimaryGroup); + free (pgrp); + if (p->gid == (gid_t)-1) + log (LOG_WARNING,"WARNING: Group not found in /etc/passwd! Using gid -1!"); + + /* Generate gid list from token group's SID list. Also look if the token + has an enabled admin group SID. That means, the process has admin + privileges. That knowledge is used in suser(). */ + PTOKEN_GROUPS gsids; + if (!(gsids = (PTOKEN_GROUPS)get_token_info (tok, TokenGroups))) + goto faulty; + if (gsids->GroupCount) + { + p->gidlist = (gid_t *) calloc (gsids->GroupCount, sizeof (gid_t)); + if (p->gidlist) + p->gidcnt = gsids->GroupCount; + } + for (DWORD i = 0; i < gsids->GroupCount; ++i) + { + if (p->gidlist) + p->gidlist[i] = cygwin_internal (CW_GET_GID_FROM_SID, + gsids->Groups[i].Sid); + if (EqualSid (gsids->Groups[i].Sid, admininstrator_group_sid) + && (gsids->Groups[i].Attributes & SE_GROUP_ENABLED)) + p->is_admin = true; + } + free (gsids); + + CloseHandle (tok); + return true; + +faulty: + CloseHandle (tok); + log (LOG_CRIT, "Failed to get token information for pid %d, winpid %d", + p->cygpid, p->winpid); + return false; +} + +/* + * Windows wrapper implementation of the VM functions called by sysv_shm.cc. + */ + +vm_object_t +_vm_pager_allocate (int size, int shmflg) +{ + /* Create the file mapping object with full access for everyone. This is + necessary to allow later calls to shmctl(..., IPC_SET,...) to + change the access rights and ownership of a shared memory region. + The access rights are tested at the beginning of every shm... function. + Note that this does not influence the actual read or write access + defined in a call to shmat. */ + vm_object_t object = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih, + PAGE_READWRITE, 0, size, NULL); + if (!object) + panic ("CreateFileMapping in _vm_pager_allocate failed, %E"); + return object; +} + +vm_object_t +vm_object_duplicate (struct thread *td, vm_object_t object) +{ + vm_object_t dup_object; + if (!DuplicateHandle(GetCurrentProcess (), object, + td->client->handle (), &dup_object, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + panic ("!DuplicateHandle in vm_object_duplicate failed, %E"); + return dup_object; +} + +void +vm_object_deallocate (vm_object_t object) +{ + if (object) + CloseHandle (object); +} + +/* + * Tunable parameters are read from a system wide cygserver.conf file. + * On the first call to tunable_int_fetch, the file is read and the + * parameters are set accordingly. Each parameter has default, max and + * min settings. + */ + +enum tun_params_type { + TUN_NULL, + TUN_INT, + TUN_BOOL +}; + +union tun_value { + long ival; + tun_bool_t bval; +}; + +struct tun_struct { + const char *name; + tun_params_type type; + union tun_value value; + union tun_value min; + union tun_value max; + void (*check_func)(tun_struct *, char *, const char *); +}; + +static void +default_tun_check (tun_struct *that, char *value, const char *fname) +{ + char *c = NULL; + tun_value val; + switch (that->type) + { + case TUN_INT: + val.ival = strtoul (value, &c, 10); + if (!val.ival || (c && *c)) + panic ("Error in config file %s: Value of parameter %s malformed", + fname, that->name); + if (val.ival < that->min.ival || val.ival > that->max.ival) + panic ("Error in config file %s: Value of parameter %s must be " + "between %lu and %lu", + fname, that->name, that->min.ival, that->max.ival); + if (that->value.ival) + panic ("Error in config file %s: Parameter %s set twice.\n", + fname, that->name); + that->value.ival = val.ival; + break; + case TUN_BOOL: + if (!strcasecmp (value, "no") || !strcasecmp (value, "n") + || !strcasecmp (value, "false") || !strcasecmp (value, "f") + || !strcasecmp (value, "0")) + val.bval = TUN_FALSE; + else if (!strcasecmp (value, "yes") || !strcasecmp (value, "y") + || !strcasecmp (value, "true") || !strcasecmp (value, "t") + || !strcasecmp (value, "1")) + val.bval = TUN_TRUE; + else + panic ("Error in config file %s: Value of parameter %s malformed\n" + "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname, that->name); + that->value.bval = val.bval; + break; + default: + /* Shouldn't happen. */ + panic ("Internal error: Wrong type of tunable parameter"); + break; + } +} + +static tun_struct tunable_params[] = +{ + /* SRV */ + { "kern.srv.cleanup_threads", TUN_INT, {0}, {1}, {16}, default_tun_check}, + { "kern.srv.request_threads", TUN_INT, {0}, {1}, {64}, default_tun_check}, + { "kern.srv.sharedmem", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + { "kern.srv.msgqueues", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + { "kern.srv.semaphores", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + + /* LOG */ + { "kern.log.syslog", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + { "kern.log.stderr", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + { "kern.log.debug", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check}, + { "kern.log.level", TUN_INT, {0}, {1}, {7}, default_tun_check}, + + /* MSG */ + { "kern.ipc.msgseg", TUN_INT, {0}, {256}, {32767}, default_tun_check}, + { "kern.ipc.msgssz", TUN_INT, {0}, {8}, {1024}, default_tun_check}, + { "kern.ipc.msgmni", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + + /* SEM */ + //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semmni", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semmns", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semmnu", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semmsl", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semopm", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semume", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check}, + { "kern.ipc.semvmx", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + { "kern.ipc.semaem", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + + /* SHM */ + { "kern.ipc.shmmaxpgs", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + { "kern.ipc.shmmni", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + { "kern.ipc.shmseg", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check}, + { NULL, TUN_NULL, {0}, {0}, {0}, NULL} +}; + +#define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c) +#define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c) +#define end_of_content(c) (!*(c) || *(c) == '#') + +void +tunable_param_init (const char *config_file, bool force) +{ + FILE *fp = fopen (config_file, "rt"); + if (!fp) + { + if (force) + panic ("can't open config file %s\n", config_file); + return; + } + char line[1024]; + while (fgets (line, 1024, fp)) + { + char *c = strrchr (line, '\n'); + if (!c) + panic ("Line too long in confg file %s\n", config_file); + /* Overwrite trailing NL. */ + *c = '\0'; + c = line; + skip_whitespace (c); + if (end_of_content (c)) + continue; + /* So we are on the first character of a parameter name. */ + char *name = c; + /* Find end of name. */ + skip_nonwhitespace (c); + if (end_of_content (c)) + { + *c++ = '\0'; + panic ("Error in config file %s: Parameter %s has no value.\n", + config_file, name); + } + /* Mark end of name. */ + *c++ = '\0'; + skip_whitespace (c); + if (end_of_content (c)) + panic ("Error in config file %s: Parameter %s has no value.\n", + config_file, name); + /* Now we are on the first character of a parameter's value. */ + char *value = c; + /* This only works for simple parameters. If complex string parameters + are added at one point, the scanning routine must be changed here. */ + /* Find end of value. */ + skip_nonwhitespace (c); + /* Mark end of value. */ + *c++ = '\0'; + /* Now look if name is one from our list. */ + tun_struct *s; + for (s = &tunable_params[0]; s->name; ++s) + if (!strcmp (name, s->name)) + { + /* Now read value and check for validity. check_func doesn't + return on error. */ + s->check_func (s, value, config_file); + break; + } + if (!s->name) + panic ("Error in config file %s: Unknown parameter %s.\n", + config_file, name); + } + fclose (fp); +} + +void +tunable_int_fetch (const char *name, long *tunable_target) +{ + tun_struct *s; + for (s = &tunable_params[0]; s->name; ++s) + if (!strcmp (name, s->name)) + break; + if (!s) /* Not found */ + return; + if (s->type != TUN_INT) /* Wrong type */ + return; + if (!s->value.ival) /* Not set in config file */ + return; + *tunable_target = s->value.ival; + debug ("\nSet %s to %lu\n", name, *tunable_target); +} + +void +tunable_bool_fetch (const char *name, tun_bool_t *tunable_target) +{ + tun_struct *s; + const char *tun_bool_val_string[] = { "undefined", "no", "yes" }; + for (s = &tunable_params[0]; s->name; ++s) + if (!strcmp (name, s->name)) + break; + if (!s) /* Not found */ + return; + if (s->type != TUN_BOOL) /* Wrong type */ + return; + if (!s->value.ival) /* Not set in config file */ + return; + *tunable_target = s->value.bval; + debug ("\nSet %s to %s\n", name, tun_bool_val_string[*tunable_target]); +} +#endif /* __OUTSIDE_CYGWIN__ */ |