diff --git a/winsup/cygserver/ChangeLog b/winsup/cygserver/ChangeLog index cb76730c4..5b9dd13c6 100644 --- a/winsup/cygserver/ChangeLog +++ b/winsup/cygserver/ChangeLog @@ -1,3 +1,76 @@ +2003-11-19 Corinna Vinschen + + 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. + 2003-10-22 Corinna Vinschen Accomodate moving cygserver header files from cygwin/include/cygwin diff --git a/winsup/cygserver/Makefile.in b/winsup/cygserver/Makefile.in index 767fcaa1f..0dd11207f 100644 --- a/winsup/cygserver/Makefile.in +++ b/winsup/cygserver/Makefile.in @@ -15,7 +15,8 @@ prefix:=@prefix@ exec_prefix:=@exec_prefix@ bindir:=@bindir@ -etcdir:=$(exec_prefix)/etc +sbindir:=@sbindir@ +sysconfdir:=@sysconfdir@ program_transform_name:=@program_transform_name@ INSTALL:=@INSTALL@ @@ -28,14 +29,18 @@ CXX:=@CXX@ CXX_FOR_TARGET:=$(CXX) AR:=@AR@ -CFLAGS:=@CFLAGS@ -I$(cygwin_source) -CXXFLAGS:=@CXXFLAGS@ -I$(cygwin_source) -override CXXFLAGS+=-fno-exceptions -fno-rtti -DHAVE_DECL_GETOPT=0 -D__OUTSIDE_CYGWIN__ - include $(srcdir)/../Makefile.common +CFLAGS:=@CFLAGS@ -I$(cygwin_source) +CXXFLAGS:=@CXXFLAGS@ -I$(cygwin_source) +override CXXFLAGS+=-MMD -fno-exceptions -fno-rtti -DHAVE_DECL_GETOPT=0 -D__OUTSIDE_CYGWIN__ -DSYSCONFDIR="\"$(sysconfdir)\"" + +.SUFFIXES: .c .cc .a .o .d + OBJS:= cygserver.o client.o process.o msg.o sem.o shm.o threaded_queue.o \ - transport.o transport_pipes.o transport_sockets.o + transport.o transport_pipes.o transport_sockets.o \ + bsd_helper.o bsd_log.o bsd_mutex.o \ + sysv_msg.o sysv_sem.o sysv_shm.o LIBOBJS:=${patsubst %.o,lib%.o,$(OBJS)} CYGWIN_OBJS:=$(cygwin_build)/smallprint.o $(cygwin_build)/version.o \ @@ -43,10 +48,17 @@ CYGWIN_OBJS:=$(cygwin_build)/smallprint.o $(cygwin_build)/version.o \ all: cygserver.exe -install: all +install: all cygserver.conf + $(INSTALL_PROGRAM) cygserver.exe $(sbindir)/cygserver.exe + $(INSTALL_DATA) $(srcdir)/cygserver.conf $(sysconfdir)/cygserver.conf clean: - rm -f $(OBJS) + rm -f $(OBJS) ${patsubst %.o,%.d,$(OBJS)} cygserver.exe + +libclean: + rm -f $(LIBOBJS) ${patsubst %.o,%.d,$(LIBOBJS)} libcygserver.a + +fullclean: clean libclean cygserver.exe: $(OBJS) $(CYGWIN_OBJS) $(CXX) -o $@ $^ @@ -64,3 +76,8 @@ lib%.o: %.cc libcygserver.a: $(LIBOBJS) $(AR) crus $@ $? + +deps:=${wildcard *.d} +ifneq (,$(deps)) +include $(deps) +endif 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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__ */ diff --git a/winsup/cygserver/bsd_helper.h b/winsup/cygserver/bsd_helper.h new file mode 100644 index 000000000..14849e918 --- /dev/null +++ b/winsup/cygserver/bsd_helper.h @@ -0,0 +1,63 @@ +/* bsd_helper.h: Helps integrating BSD kernel code + + 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. */ +#ifndef _BSD_HELPER_H +#define _BSD_HELPER_H + +#include +#include + +enum tun_bool_t { + TUN_UNDEF = 0, + TUN_FALSE = 1, + TUN_TRUE = 2 +}; + +#define TUNABLE_INT_FETCH(a,b) tunable_int_fetch((a),(b)) +#define TUNABLE_BOOL_FETCH(a,b) tunable_bool_fetch((a),(b)) + +#define sys_malloc(a,b,c) (malloc(a)?:(panic("malloc failed in %s, line %d"),(void*)NULL)) +#define sys_free(a,b) free(a) + +#define jail_sysvipc_allowed true +#define jailed(a) false + +extern const char *__progname; + +/* Global vars, determining whether the IPC stuff should be started or not. */ +extern tun_bool_t support_sharedmem; +extern tun_bool_t support_msgqueues; +extern tun_bool_t support_semaphores; + +extern SECURITY_ATTRIBUTES sec_all_nih; + +int win_copyin (struct thread *, const void *, void *, size_t); +int win_copyout (struct thread *, const void *, void *, size_t); +#define copyin(a,b,c) win_copyin((td),(a),(b),(c)) +#define copyout(a,b,c) win_copyout((td),(a),(b),(c)) + +int ipcperm (struct thread *, struct ipc_perm *, unsigned int); +int suser (struct thread *); +bool adjust_identity_info (struct proc *p); + +struct vmspace *ipc_p_vmspace (struct proc *); +int ipcexit_creat_hookthread(struct thread *); +void ipcinit (void); +int ipcunload (void); + +vm_object_t _vm_pager_allocate (int, int); +#define vm_pager_allocate(a,b,s,c,d) _vm_pager_allocate((s),(mode)) +vm_object_t vm_object_duplicate (struct thread *td, vm_object_t object); +void vm_object_deallocate (vm_object_t object); + +void tunable_param_init (const char *, bool); +void tunable_int_fetch (const char *, long *); +void tunable_bool_fetch (const char *, tun_bool_t *); + +#endif /* _BSD_HELPER_H */ diff --git a/winsup/cygserver/bsd_log.cc b/winsup/cygserver/bsd_log.cc new file mode 100644 index 000000000..5cf1c848e --- /dev/null +++ b/winsup/cygserver/bsd_log.cc @@ -0,0 +1,95 @@ +/* bsd_log.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" +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include +#include +#include + +long log_level = 8; /* Illegal value. Don't change! */ +tun_bool_t log_debug = TUN_UNDEF; +tun_bool_t log_syslog = TUN_UNDEF; +tun_bool_t log_stderr = TUN_UNDEF; + +void +loginit (tun_bool_t opt_stderr, tun_bool_t opt_syslog) +{ + if (log_debug == TUN_UNDEF) + TUNABLE_BOOL_FETCH ("kern.log.debug", &log_debug); + if (log_debug == TUN_UNDEF) + log_debug = TUN_FALSE; + + if (opt_stderr != TUN_UNDEF) + log_stderr = opt_stderr; + else + TUNABLE_BOOL_FETCH ("kern.log.stderr", &log_stderr); + if (log_stderr == TUN_UNDEF) + log_stderr = TUN_FALSE; + + if (opt_syslog != TUN_UNDEF) + log_syslog = opt_syslog; + else + TUNABLE_BOOL_FETCH ("kern.log.syslog", &log_syslog); + if (log_syslog == TUN_UNDEF) + log_syslog = TUN_FALSE; + + if (log_level == 8) + TUNABLE_INT_FETCH ("kern.log.level", &log_level); + if (log_level == 8) + log_level = 6; +} + +void +_vlog (const char *file, int line, int level, + const char *fmt, va_list ap) +{ + char buf[16384]; + + if ((level == LOG_DEBUG && log_debug != TUN_TRUE) + || (level != LOG_DEBUG && level >= log_level)) + return; + strcpy (buf, "cygserver: "); + if (file && log_debug == TUN_TRUE) + __small_sprintf (strchr (buf, '\0'), "%s, line %d: ", file, line); + __small_vsprintf (strchr (buf, '\0'), fmt, ap); + if (log_syslog == TUN_TRUE && level != LOG_DEBUG) + syslog (level, buf); + if (log_stderr == TUN_TRUE || level == LOG_DEBUG) + { + fputs (buf, stderr); + fputc ('\n', stderr); + } +} + +void +_log (const char *file, int line, int level, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + _vlog (file, line, level, fmt, ap); +} + +void +_vpanic (const char *file, int line, const char *fmt, va_list ap) +{ + _vlog (file, line, LOG_EMERG, fmt, ap); + exit (1); +} + +void +_panic (const char *file, int line, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + _vpanic (file, line, fmt, ap); +} +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/bsd_log.h b/winsup/cygserver/bsd_log.h new file mode 100644 index 000000000..48573182a --- /dev/null +++ b/winsup/cygserver/bsd_log.h @@ -0,0 +1,33 @@ +/* bsd_log.h: Helps integrating BSD kernel code + + 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. */ +#ifndef _BSD_LOG_H +#define _BSD_LOG_H + +#include +#include + +extern long log_level; +extern tun_bool_t log_debug; +extern tun_bool_t log_syslog; +extern tun_bool_t log_stderr; + +void loginit (tun_bool_t, tun_bool_t); +void _vlog (const char *, int, int, const char *, va_list); +void _log (const char *, int, int, const char *, ...); +void _vpanic (const char *, int, const char *, va_list) __attribute__ ((noreturn)); +void _panic (const char *, int, const char *, ...) __attribute__ ((noreturn)); +#define vlog(l,f,a) _vlog(NULL,0,(l),(f),(a)) +#define log(l,f,...) _log(NULL,0,(l),(f),##__VA_ARGS__) +#define vdebug(f,a) _vlog(__FILE__,__LINE__,LOG_DEBUG,(f),(a)) +#define debug(f,...) _log(__FILE__,__LINE__,LOG_DEBUG,(f),##__VA_ARGS__) +#define vpanic(f,a) _vpanic(__FILE__,__LINE__,(f),(a)) +#define panic(f,...) _panic(__FILE__,__LINE__,(f),##__VA_ARGS__) + +#endif /* _BSD_LOG_H */ diff --git a/winsup/cygserver/bsd_mutex.cc b/winsup/cygserver/bsd_mutex.cc new file mode 100644 index 000000000..9c7485b7f --- /dev/null +++ b/winsup/cygserver/bsd_mutex.cc @@ -0,0 +1,253 @@ +/* bsd_mutex.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 + +#include "process.h" +#include "cygserver_ipc.h" + +/* A BSD kernel global mutex. */ +struct mtx Giant; + +void +mtx_init (mtx *m, const char *name, const void *, int) +{ + m->name = name; + m->owner = 0; + /* Can't use Windows Mutexes here since Windows Mutexes are only + unlockable by the lock owner. */ + m->h = CreateSemaphore (NULL, 1, 1, NULL); + if (!m->h) + panic ("couldn't allocate %s mutex, %E\n", name); +} + +void +_mtx_lock (mtx *m, DWORD winpid, const char *file, int line) +{ + _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name); + if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0) + _panic (file, line, "wait for %s in %d failed, %E", m->name, winpid); + m->owner = winpid; + _log (file, line, LOG_DEBUG, "Locked mutex %s", m->name); +} + +int +mtx_owned (mtx *m) +{ + return m->owner > 0; +} + +void +_mtx_assert(mtx *m, int what, const char *file, int line) +{ + switch (what) + { + case MA_OWNED: + if (!mtx_owned (m)) + _panic(file, line, "Mutex %s not owned", m->name); + break; + case MA_NOTOWNED: + if (mtx_owned (m)) + _panic(file, line, "Mutex %s is owned", m->name); + break; + default: + break; + } +} + +void +_mtx_unlock (mtx *m, const char *file, int line) +{ + m->owner = 0; + /* Cautiously check if mtx_destroy has been called (shutdown). + In that case, m->h is NULL. */ + if (m->h && !ReleaseSemaphore (m->h, 1, NULL)) + { + /* Check if the semaphore was already on it's max value. In this case, + ReleaseSemaphore returns FALSE with an error code which *sic* depends + on the OS. */ + if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_PARAMETER) + || (wincap.is_winnt () && GetLastError () != ERROR_TOO_MANY_POSTS)) + _panic (file, line, "release of mutex %s failed, %E", m->name); + } + _log (file, line, LOG_DEBUG, "Unlocked mutex %s", m->name); +} + +void +mtx_destroy (mtx *m) +{ + HANDLE tmp = m->h; + m->h = NULL; + if (tmp) + CloseHandle (tmp); +} + +/* + * Helper functions for msleep/wakeup. + */ +static char * +msleep_event_name (void *ident, char *name) +{ + if (wincap.has_terminal_services ()) + __small_sprintf (name, "Global\\cygserver.msleep.evt.%08x", ident); + else + __small_sprintf (name, "cygserver.msleep.evt.%08x", ident); + return name; +} + +/* + * Original description from BSD code: + * + * General sleep call. Suspends the current process until a wakeup is + * performed on the specified identifier. The process will then be made + * runnable with the specified priority. Sleeps at most timo/hz seconds + * (0 means no timeout). If pri includes PCATCH flag, signals are checked + * before and after sleeping, else signals are not checked. Returns 0 if + * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a + * signal needs to be delivered, ERESTART is returned if the current system + * call should be restarted if possible, and EINTR is returned if the system + * call should be interrupted by the signal (return EINTR). + * + * The mutex argument is exited before the caller is suspended, and + * entered before msleep returns. If priority includes the PDROP + * flag the mutex is not entered before returning. + */ +static HANDLE msleep_glob_evt; + +void +msleep_init (void) +{ + msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); + if (!msleep_glob_evt) + panic ("CreateEvent in msleep_init failed: %E"); +} + +static int +win_priority (int priority) +{ + int p = (int)((p) & PRIO_MASK) - PZERO; + /* Generating a valid priority value is a bit tricky. The only valid + values on 9x and NT4 are -15, -2, -1, 0, 1, 2, 15. */ + switch (p) + { + case -15: case -14: case -13: case -12: case -11: + return THREAD_PRIORITY_IDLE; + case -10: case -9: case -8: case -7: case -6: + return THREAD_PRIORITY_LOWEST; + case -5: case -4: case -3: case -2: case -1: + return THREAD_PRIORITY_BELOW_NORMAL; + case 0: + return THREAD_PRIORITY_NORMAL; + case 1: case 2: case 3: case 4: case 5: + return THREAD_PRIORITY_ABOVE_NORMAL; + case 6: case 7: case 8: case 9: case 10: + return THREAD_PRIORITY_HIGHEST; + case 11: case 12: case 13: case 14: case 15: + return THREAD_PRIORITY_TIME_CRITICAL; + } + return THREAD_PRIORITY_NORMAL; +} + +/* + * Sets the thread priority, returns the old priority. + */ +static int +set_priority (int priority) +{ + int old_prio = GetThreadPriority (GetCurrentThread ()); + if (!SetThreadPriority (GetCurrentThread (), win_priority(priority))) + log (LOG_WARNING, + "Warning: Setting thread priority to %d failed with error %lu\n", + win_priority(priority), GetLastError ()); + return old_prio; +} + +int +_msleep (void *ident, struct mtx *mtx, int priority, + const char *wmesg, int timo, struct thread *td) +{ + int ret = -1; + char name[64]; + msleep_event_name (ident, name); + HANDLE evt = OpenEvent (EVENT_ALL_ACCESS, FALSE, name); + if (!evt) + evt = CreateEvent (NULL, TRUE, FALSE, name); + if (!evt) + panic ("CreateEvent in msleep (%s) failed: %E", wmesg); + if (mtx) + mtx_unlock (mtx); + int old_priority = set_priority (priority); + /* PCATCH can't be handled here. */ + HANDLE obj[3] = { evt, td->client->handle (), msleep_glob_evt }; + switch (WaitForMultipleObjects (3, obj, FALSE, timo ?: INFINITE)) + { + case WAIT_OBJECT_0: /* wakeup() has been called. */ + ret = 0; + break; + case WAIT_OBJECT_0 + 2: /* Shutdown event (triggered by wakeup_all). */ + priority |= PDROP; + /*FALLTHRU*/ + case WAIT_OBJECT_0 + 1: /* The dependent process has exited. */ + ret = EIDRM; + break; + case WAIT_TIMEOUT: + ret = EWOULDBLOCK; + break; + default: + panic ("wait in msleep (%s) failed, %E", wmesg); + break; + } + set_priority (old_priority); + if (!(priority & PDROP) && mtx) + mtx_lock (mtx); + CloseHandle (evt); + return ret; +} + +/* + * Make all threads sleeping on the specified identifier runnable. + */ +int +wakeup (void *ident) +{ + char name[64]; + msleep_event_name (ident, name); + HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name); + if (!evt) + { + /* Another round of different error codes returned by 9x and NT + systems. Oh boy... */ + if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME) + || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND)) + panic ("OpenEvent (%s) in wakeup failed: %E", name); + } + if (evt) + { + if (!SetEvent (evt)) + panic ("SetEvent (%s) in wakeup failed, %E", name); + CloseHandle (evt); + } + return 0; +} + +/* + * Wakeup all sleeping threads. Only called in the context of cygserver + * shutdown. + */ +void +wakeup_all (void) +{ + SetEvent (msleep_glob_evt); +} +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/bsd_mutex.h b/winsup/cygserver/bsd_mutex.h new file mode 100644 index 000000000..3b07bf3c6 --- /dev/null +++ b/winsup/cygserver/bsd_mutex.h @@ -0,0 +1,51 @@ +/* bsd_mutex.h: BSD Mutex helper + + 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. */ +#ifndef _BSD_MUTEX_H +#define _BSD_MUTEX_H + +#define MTX_DEF 0 + +#define MA_OWNED 1 +#define MA_NOTOWNED 2 + +#define PZERO (0x20) +#define PRIO_MASK (0x1f) +#define PDROP 0x1000 +#define PCATCH 0x2000 +#define PLOCK 0x3000 + +struct mtx { + HANDLE h; + const char *name; + DWORD owner; +}; + +/* Some BSD kernel global mutex. */ +extern struct mtx Giant; + +void mtx_init (mtx *, const char *, const void *, int); +void _mtx_lock (mtx *, DWORD winpid, const char *, int); +#define mtx_lock(m) _mtx_lock((m), (td->ipcblk->winpid), __FILE__, __LINE__) +int mtx_owned (mtx *); +void _mtx_assert(mtx *, int, const char *, int); +#define mtx_assert(m,w) _mtx_assert((m),(w),__FILE__,__LINE__) +void _mtx_unlock (mtx *, const char *, int); +#define mtx_unlock(m) _mtx_unlock((m),__FILE__,__LINE__) + +void mtx_destroy (mtx *); + +void msleep_init (void); +int _msleep (void *, struct mtx *, int, const char *, int, struct thread *); +#define msleep(i,m,p,w,t) _msleep((i),(m),(p),(w),(t),(td)) +#define tsleep(i,p,w,t) _msleep((i),NULL,(p),(w),(t),(td)) +int wakeup (void *); +void wakeup_all (void); + +#endif /* _BSD_MUTEX_H */ diff --git a/winsup/cygserver/client.cc b/winsup/cygserver/client.cc index 600ddbd67..d8f7ac55a 100644 --- a/winsup/cygserver/client.cc +++ b/winsup/cygserver/client.cc @@ -1,6 +1,6 @@ -/* cygserver_client.cc +/* client.cc - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Egor Duda @@ -22,11 +22,12 @@ details. */ #include #include "cygerrno.h" +#include "cygserver_msg.h" +#include "cygserver_sem.h" #include "cygserver_shm.h" -#include "safe_memory.h" #include "cygserver.h" -#include "cygserver_transport.h" +#include "transport.h" int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children. @@ -48,6 +49,8 @@ client_request_get_version::client_request_get_version () * the first numbers match, that is). */ +#ifdef __INSIDE_CYGWIN__ + bool client_request_get_version::check_version () const { @@ -71,8 +74,6 @@ client_request_get_version::check_version () const return ok; } -#ifdef __INSIDE_CYGWIN__ - client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid, HANDLE nfrom_master, HANDLE nto_master) @@ -87,15 +88,6 @@ client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid, "from_master = %lu, to_master = %lu"), req.pid, req.master_pid, req.from_master, req.to_master); } - -#else /* !__INSIDE_CYGWIN__ */ - -client_request_attach_tty::client_request_attach_tty () - : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) -{ - // verbose: syscall_printf ("created"); -} - #endif /* __INSIDE_CYGWIN__ */ /* @@ -230,7 +222,12 @@ client_request::send (transport_layer_base * const conn) // sizeof (_header), msglen ()); } -#ifndef __INSIDE_CYGWIN__ +#ifdef __OUTSIDE_CYGWIN__ + +client_request_attach_tty::client_request_attach_tty () + : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) +{ +} /* * client_request::handle_request () @@ -277,16 +274,22 @@ client_request::handle_request (transport_layer_base *const conn, switch (header.request_code) { case CYGSERVER_REQUEST_GET_VERSION: - req = safe_new0 (client_request_get_version); + req = new client_request_get_version; break; case CYGSERVER_REQUEST_SHUTDOWN: - req = safe_new0 (client_request_shutdown); + req = new client_request_shutdown; break; case CYGSERVER_REQUEST_ATTACH_TTY: - req = safe_new0 (client_request_attach_tty); + req = new client_request_attach_tty; + break; + case CYGSERVER_REQUEST_MSG: + req = new client_request_msg; + break; + case CYGSERVER_REQUEST_SEM: + req = new client_request_sem; break; case CYGSERVER_REQUEST_SHM: - req = safe_new0 (client_request_shm); + req = new client_request_shm; break; default: syscall_printf ("unknown request code %d received: request ignored", @@ -299,74 +302,9 @@ client_request::handle_request (transport_layer_base *const conn, req->msglen (header.msglen); req->handle (conn, cache); - safe_delete (req); - -#ifndef DEBUGGING - printf ("."); // A little noise when we're being quiet. -#endif + delete req; } -#endif /* !__INSIDE_CYGWIN__ */ - -client_request::client_request (request_code_t const id, - void * const buf, - size_t const buflen) - : _header (id, buflen), - _buf (buf), - _buflen (buflen) -{ - assert ((!_buf && !_buflen) || (_buf && _buflen)); -} - -client_request::~client_request () -{} - -int -client_request::make_request () -{ - assert (cygserver_running == CYGSERVER_UNKNOWN \ - || cygserver_running == CYGSERVER_OK \ - || cygserver_running == CYGSERVER_UNAVAIL); - - if (cygserver_running == CYGSERVER_UNKNOWN) - cygserver_init (); - - assert (cygserver_running == CYGSERVER_OK \ - || cygserver_running == CYGSERVER_UNAVAIL); - - /* Don't retry every request if the server's not there */ - if (cygserver_running == CYGSERVER_UNAVAIL) - { - syscall_printf ("cygserver un-available"); - error_code (ENOSYS); - return -1; - } - - transport_layer_base *const transport = create_server_transport (); - - assert (transport); - - if (transport->connect () == -1) - { - if (errno) - error_code (errno); - else - error_code (ENOSYS); - safe_delete (transport); - return -1; - } - - // verbose: debug_printf ("connected to server %p", transport); - - send (transport); - - safe_delete (transport); - - return 0; -} - -#ifndef __INSIDE_CYGWIN__ - /* * client_request::handle () * @@ -470,7 +408,84 @@ client_request::handle (transport_layer_base *const conn, // sizeof (_header), msglen ()); } -#endif /* !__INSIDE_CYGWIN__ */ +/* The server side implementation of make_request. Very simple. */ +int +client_request::make_request () +{ + transport_layer_base *const transport = create_server_transport (); + assert (transport); + if (transport->connect () == -1) + { + if (errno) + error_code (errno); + else + error_code (ENOSYS); + delete transport; + return -1; + } + send (transport); + delete transport; + return 0; +} +#endif /* __OUTSIDE_CYGWIN__ */ + +client_request::client_request (request_code_t const id, + void * const buf, + size_t const buflen) + : _header (id, buflen), + _buf (buf), + _buflen (buflen) +{ + assert ((!_buf && !_buflen) || (_buf && _buflen)); +} + +client_request::~client_request () +{} + +#ifdef __INSIDE_CYGWIN__ +int +client_request::make_request () +{ + assert (cygserver_running == CYGSERVER_UNKNOWN \ + || cygserver_running == CYGSERVER_OK \ + || cygserver_running == CYGSERVER_UNAVAIL); + + if (cygserver_running == CYGSERVER_UNKNOWN) + cygserver_init (); + + assert (cygserver_running == CYGSERVER_OK \ + || cygserver_running == CYGSERVER_UNAVAIL); + + /* Don't retry every request if the server's not there */ + if (cygserver_running == CYGSERVER_UNAVAIL) + { + syscall_printf ("cygserver un-available"); + error_code (ENOSYS); + return -1; + } + + transport_layer_base *const transport = create_server_transport (); + + assert (transport); + + if (transport->connect () == -1) + { + if (errno) + error_code (errno); + else + error_code (ENOSYS); + delete transport; + return -1; + } + + // verbose: debug_printf ("connected to server %p", transport); + + send (transport); + + delete transport; + + return 0; +} bool check_cygserver_available () @@ -523,3 +538,4 @@ cygserver_init () if (!check_cygserver_available ()) cygserver_running = CYGSERVER_UNAVAIL; } +#endif /* __INSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/cygserver.cc b/winsup/cygserver/cygserver.cc index f152fc588..d38d12eee 100644 --- a/winsup/cygserver/cygserver.cc +++ b/winsup/cygserver/cygserver.cc @@ -1,6 +1,6 @@ /* cygserver.cc - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Egor Duda @@ -10,6 +10,7 @@ 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 @@ -27,100 +28,21 @@ details. */ #include "cygwin_version.h" #include "cygserver.h" -#include "cygserver_process.h" -#include "cygserver_transport.h" +#include "process.h" +#include "transport.h" + +#include "cygserver_ipc.h" +#include "cygserver_msg.h" +#include "cygserver_sem.h" + +#define DEF_CONFIG_FILE "" SYSCONFDIR "/cygserver.conf" // Version string. static const char version[] = "$Revision$"; -/* - * Support function for the XXX_printf () macros in "woutsup.h". - * Copied verbatim from "strace.cc". - */ -static int -getfunc (char *in_dst, const char *func) -{ - const char *p; - const char *pe; - char *dst = in_dst; - for (p = func; (pe = strchr (p, '(')); p = pe + 1) - if (isalnum ((int)pe[-1]) || pe[-1] == '_') - break; - else if (isspace ((int)pe[-1])) - { - pe--; - break; - } - if (!pe) - pe = strchr (func, '\0'); - for (p = pe; p > func; p--) - if (p != pe && *p == ' ') - { - p++; - break; - } - if (*p == '*') - p++; - while (p < pe) - *dst++ = *p++; - - *dst++ = ':'; - *dst++ = ' '; - *dst = '\0'; - - return dst - in_dst; -} - -/* - * Support function for the XXX_printf () macros in "woutsup.h". - */ -extern "C" void -__cygserver__printf (const char *const function, const char *const fmt, ...) -{ - const DWORD lasterror = GetLastError (); - const int lasterrno = errno; - - va_list ap; - - char *const buf = (char *) alloca (BUFSIZ); - - assert (buf); - - int len = 0; - - if (function) - len += getfunc (buf, function); - - va_start (ap, fmt); - len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap); - va_end (ap); - - len += snprintf (buf + len, BUFSIZ - len, "\n"); - - const int actual = (len > BUFSIZ ? BUFSIZ : len); - - write (2, buf, actual); - - errno = lasterrno; - SetLastError (lasterror); - - return; -} - -#ifdef DEBUGGING - -int __stdcall -__set_errno (const char *func, int ln, int val) -{ - debug_printf ("%s:%d val %d", func, ln, val); - return _impure_ptr->_errno = val; -} - -#endif /* DEBUGGING */ - GENERIC_MAPPING access_mapping; -static BOOL +static bool setup_privileges () { BOOL rc, ret_val; @@ -130,15 +52,14 @@ setup_privileges () rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ; if (!rc) { - system_printf ("error opening process token (%lu)", GetLastError ()); - ret_val = FALSE; - goto out; + debug ("error opening process token (%lu)", GetLastError ()); + return false; } rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid); if (!rc) { - system_printf ("error getting privilege luid (%lu)", GetLastError ()); - ret_val = FALSE; + debug ("error getting privilege luid (%lu)", GetLastError ()); + ret_val = false; goto out; } sPrivileges.PrivilegeCount = 1 ; @@ -146,9 +67,8 @@ setup_privileges () rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ; if (!rc) { - system_printf ("error adjusting privilege level. (%lu)", - GetLastError ()); - ret_val = FALSE; + debug ("error adjusting privilege level. (%lu)", GetLastError ()); + ret_val = false; goto out; } @@ -157,7 +77,7 @@ setup_privileges () access_mapping.GenericExecute = 0; access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; - ret_val = TRUE; + ret_val = true; out: CloseHandle (hToken); @@ -181,7 +101,7 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process, 0, bInheritHandle, DUPLICATE_SAME_ACCESS)) { - system_printf ("error getting handle(%u) to server (%lu)", + log (LOG_ERR, "error getting handle(%u) to server (%lu)", (unsigned int)from_handle, GetLastError ()); goto out; } @@ -205,7 +125,7 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process, | DACL_SECURITY_INFORMATION), sd, sizeof (sd_buf), &bytes_needed)) { - system_printf ("error getting handle SD (%lu)", GetLastError ()); + log (LOG_ERR, "error getting handle SD (%lu)", GetLastError ()); goto out; } @@ -214,14 +134,14 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process, if (!AccessCheck (sd, from_process_token, access, &access_mapping, &ps, &ps_len, &access, &status)) { - system_printf ("error checking access rights (%lu)", + log (LOG_ERR, "error checking access rights (%lu)", GetLastError ()); goto out; } if (!status) { - system_printf ("access to object denied"); + log (LOG_ERR, "access to object denied"); goto out; } } @@ -230,11 +150,11 @@ check_and_dup_handle (HANDLE from_process, HANDLE to_process, to_process, to_handle_ptr, access, bInheritHandle, 0)) { - system_printf ("error getting handle to client (%lu)", GetLastError ()); + log (LOG_ERR, "error getting handle to client (%lu)", GetLastError ()); goto out; } - // verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr); + debug ("Duplicated %p to %p", from_handle, *to_handle_ptr); ret_val = 0; @@ -259,7 +179,7 @@ client_request_attach_tty::serve (transport_layer_base *const conn, if (!wincap.has_security ()) { - syscall_printf ("operation only supported on systems with security"); + log (LOG_NOTICE, "operation only supported on systems with security"); error_code (EINVAL); msglen (0); return; @@ -267,7 +187,7 @@ client_request_attach_tty::serve (transport_layer_base *const conn, if (msglen () != sizeof (req)) { - syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + log (LOG_ERR, "bad request body length: expecting %lu bytes, got %lu", sizeof (req), msglen ()); error_code (EINVAL); msglen (0); @@ -276,54 +196,65 @@ client_request_attach_tty::serve (transport_layer_base *const conn, msglen (0); // Until we fill in some fields. - // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld", - // req.master_pid, req.from_master, req.to_master, - // req.pid); + debug ("pid %ld:(%p,%p) -> pid %ld", req.master_pid, req.from_master, + req.to_master, req.pid); - // verbose: debug_printf ("opening process %ld", req.master_pid); + debug ("opening process %ld", req.master_pid); const HANDLE from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); if (!from_process_handle) { - system_printf ("error opening `from' process, error = %lu", + log (LOG_ERR, "error opening `from' process, error = %lu", GetLastError ()); error_code (EACCES); return; } - // verbose: debug_printf ("opening process %ld", req.pid); + debug ("opening process %ld", req.pid); const HANDLE to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); if (!to_process_handle) { - system_printf ("error opening `to' process, error = %lu", + log (LOG_ERR, "error opening `to' process, error = %lu", GetLastError ()); CloseHandle (from_process_handle); error_code (EACCES); return; } - // verbose: debug_printf ("Impersonating client"); - conn->impersonate_client (); + debug ("Impersonating client"); + if (!conn->impersonate_client ()) + { + CloseHandle (from_process_handle); + CloseHandle (to_process_handle); + error_code (EACCES); + return; + } HANDLE token_handle = NULL; - // verbose: debug_printf ("about to open thread token"); + debug ("about to open thread token"); const DWORD rc = OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE, &token_handle); - // verbose: debug_printf ("opened thread token, rc=%lu", rc); - conn->revert_to_self (); + debug ("opened thread token, rc=%lu", rc); + if (!conn->revert_to_self ()) + { + CloseHandle (from_process_handle); + CloseHandle (to_process_handle); + error_code (EACCES); + return; + } if (!rc) { - system_printf ("error opening thread token, error = %lu", + log (LOG_ERR, "error opening thread token, error = %lu", GetLastError ()); CloseHandle (from_process_handle); CloseHandle (to_process_handle); @@ -348,7 +279,7 @@ client_request_attach_tty::serve (transport_layer_base *const conn, from_master, &req.from_master, TRUE) != 0) { - system_printf ("error duplicating from_master handle, error = %lu", + log (LOG_ERR, "error duplicating from_master handle, error = %lu", GetLastError ()); error_code (EACCES); } @@ -360,7 +291,7 @@ client_request_attach_tty::serve (transport_layer_base *const conn, to_master, &req.to_master, TRUE) != 0) { - system_printf ("error duplicating to_master handle, error = %lu", + log (LOG_ERR, "error duplicating to_master handle, error = %lu", GetLastError ()); error_code (EACCES); } @@ -369,7 +300,7 @@ client_request_attach_tty::serve (transport_layer_base *const conn, CloseHandle (to_process_handle); CloseHandle (token_handle); - debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)", + debug ("%lu(%lu, %lu) -> %lu(%lu,%lu)", req.master_pid, from_master, to_master, req.pid, req.from_master, req.to_master); @@ -382,7 +313,7 @@ client_request_get_version::serve (transport_layer_base *, process_cache *) assert (!error_code ()); if (msglen ()) - syscall_printf ("unexpected request body ignored: %lu bytes", msglen ()); + log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ()); msglen (sizeof (version)); @@ -401,7 +332,7 @@ public: virtual ~server_request () { - safe_delete (_conn); + delete _conn; } virtual void process () @@ -455,8 +386,8 @@ server_submission_loop::request_loop () */ if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1)) if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST)) - debug_printf ("failed to raise accept thread priority, error = %lu", - GetLastError ()); + debug ("failed to raise accept thread priority, error = %lu", + GetLastError ()); while (_running) { @@ -464,7 +395,7 @@ server_submission_loop::request_loop () transport_layer_base *const conn = _transport->accept (&recoverable); if (!conn && !recoverable) { - system_printf ("fatal error on IPC transport: closing down"); + log (LOG_ERR, "fatal error on IPC transport: closing down"); return; } // EINTR probably implies a shutdown request; so back off for a @@ -474,26 +405,25 @@ server_submission_loop::request_loop () if (!conn && errno == EINTR) { if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL)) - debug_printf ("failed to reset thread priority, error = %lu", - GetLastError ()); + debug ("failed to reset thread priority, error = %lu", + GetLastError ()); Sleep (0); if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1)) if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST)) - debug_printf ("failed to raise thread priority, error = %lu", - GetLastError ()); + debug ("failed to raise thread priority, error = %lu", + GetLastError ()); } if (conn) - _queue->add (safe_new (server_request, conn, _cache)); + _queue->add (new server_request (conn, _cache)); } } client_request_shutdown::client_request_shutdown () : client_request (CYGSERVER_REQUEST_SHUTDOWN) { - // verbose: syscall_printf ("created"); } void @@ -502,7 +432,7 @@ client_request_shutdown::serve (transport_layer_base *, process_cache *) assert (!error_code ()); if (msglen ()) - syscall_printf ("unexpected request body ignored: %lu bytes", msglen ()); + log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ()); /* FIXME: link upwards, and then this becomes a trivial method call to * only shutdown _this queue_ @@ -530,12 +460,33 @@ handle_signal (const int signum) static void print_usage (const char *const pgm) { - printf ("Usage: %s [OPTIONS]\n", pgm); - printf (" -c, --cleanup-threads number of cleanup threads to use\n"); - printf (" -h, --help output usage information and exit\n"); - printf (" -r, --request-threads number of request threads to use\n"); - printf (" -s, --shutdown shutdown the daemon\n"); - printf (" -v, --version output version information and exit\n"); + log (LOG_NOTICE, "Usage: %s [OPTIONS]\n" +"Configuration option:\n" +" -f, --config-file Use as config file. Default is\n" +"\n" +"Performance options:\n" +" -c, --cleanup-threads Number of cleanup threads to use.\n" +" -r, --request-threads Number of request threads to use.\n" +"\n" +"Logging options:\n" +" -d, --debug Log debug messages to stderr.\n" +" -e, --stderr Log to stderr (default if stderr is a tty).\n" +" -E, --no-stderr Don't log to stderr (see -y, -Y options).\n" +" " DEF_CONFIG_FILE "\n" +" -l, --log-level Verbosity of logging (1..7). Default: 6\n" +" -y, --syslog Log to syslog (default if stderr is no tty).\n" +" -Y, --no-syslog Don't log to syslog (See -e, -E options).\n" +"\n" +"Support options:\n" +" -m, --no-sharedmem Don't start XSI Shared Memory support.\n" +" -q, --no-msgqueues Don't start XSI Message Queue support.\n" +" -s, --no-semaphores Don't start XSI Semaphore support.\n" +"\n" +"Miscellaneous:\n" +" -S, --shutdown Shutdown the daemon.\n" +" -h, --help Output usage information and exit.\n" +" -v, --version Output version information and exit." +, pgm); } /* @@ -543,7 +494,7 @@ print_usage (const char *const pgm) */ static void -print_version (const char *const pgm) +print_version () { char *vn = NULL; @@ -578,10 +529,12 @@ print_version (const char *const pgm) cygwin_version.mount_registry, cygwin_version.dll_build_date); - printf ("%s (cygwin) %s\n", pgm, vn); - printf ("API version %s\n", buf); - printf ("Copyright 2001, 2002 Red Hat, Inc.\n"); - printf ("Compiled on %s\n", __DATE__); + log (LOG_INFO, "(cygwin) %s\n" + "API version %s\n" + "Copyright 2001, 2002, 2003 Red Hat, Inc.\n" + "Compiled on %s\n" + "Default configuration file is %s", + vn, buf, __DATE__, DEF_CONFIG_FILE); free (vn); } @@ -595,79 +548,127 @@ main (const int argc, char *argv[]) { const struct option longopts[] = { {"cleanup-threads", required_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, + {"stderr", no_argument, NULL, 'e'}, + {"no-stderr", no_argument, NULL, 'E'}, + {"config-file", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, + {"log-level", required_argument, NULL, 'l'}, + {"no-sharedmem", no_argument, NULL, 'm'}, + {"no-msgqueues", no_argument, NULL, 'q'}, {"request-threads", required_argument, NULL, 'r'}, - {"shutdown", no_argument, NULL, 's'}, + {"no-semaphores", no_argument, NULL, 's'}, + {"shutdown", no_argument, NULL, 'S'}, {"version", no_argument, NULL, 'v'}, + {"syslog", no_argument, NULL, 'y'}, + {"no-syslog", no_argument, NULL, 'Y'}, {0, no_argument, NULL, 0} }; - const char opts[] = "c:hr:sv"; + const char opts[] = "c:deEf:hl:mqr:sSvyY"; - int cleanup_threads = 2; - int request_threads = 10; + long cleanup_threads = 0; + long request_threads = 0; bool shutdown = false; + const char *config_file = DEF_CONFIG_FILE; + bool force_config_file = false; + tun_bool_t option_log_stderr = TUN_UNDEF; + tun_bool_t option_log_syslog = TUN_UNDEF; - const char *pgm = NULL; + char *c = NULL; - if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/'))) - pgm = *argv; + /* Check if we have a terminal. If so, default to stderr logging, + otherwise default to syslog logging. This must be done early + to allow default logging already in option processing state. */ + openlog ("cygserver", LOG_PID, LOG_KERN); + if (isatty (2)) + log_stderr = TUN_TRUE; else - pgm++; - - wincap.init (); - if (wincap.has_security ()) - setup_privileges (); + log_syslog = TUN_TRUE; int opt; + opterr = 0; while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (opt) { case 'c': - cleanup_threads = atoi (optarg); - if (cleanup_threads <= 0) - { - fprintf (stderr, - "%s: number of cleanup threads must be positive\n", - pgm); - exit (1); - } + c = NULL; + cleanup_threads = strtol (optarg, &c, 10); + if (cleanup_threads <= 0 || cleanup_threads > 16 || (c && *c)) + panic ("Number of cleanup threads must be between 1 and 16"); break; + case 'd': + log_debug = TUN_TRUE; + break; + + case 'e': + option_log_stderr = TUN_TRUE; + break; + + case 'E': + option_log_stderr = TUN_FALSE; + break; + + case 'f': + config_file = optarg; + force_config_file = true; + break; + case 'h': - print_usage (pgm); + print_usage (getprogname ()); return 0; + case 'l': + c = NULL; + log_level = strtoul (optarg, &c, 10); + if (!log_level || log_level > 7 || (c && *c)) + panic ("Log level must be between 1 and 7"); + break; + + case 'm': + support_sharedmem = TUN_FALSE; + break; + + case 'q': + support_msgqueues = TUN_FALSE; + break; + case 'r': - request_threads = atoi (optarg); - if (request_threads <= 0) - { - fprintf (stderr, - "%s: number of request threads must be positive\n", - pgm); - exit (1); - } + c = NULL; + request_threads = strtol (optarg, &c, 10); + if (request_threads <= 0 || request_threads > 64 || (c && *c)) + panic ("Number of request threads must be between 1 and 64"); break; case 's': + support_semaphores = TUN_FALSE; + break; + + case 'S': shutdown = true; break; case 'v': - print_version (pgm); + print_version (); return 0; + case 'y': + option_log_syslog = TUN_TRUE; + break; + + case 'Y': + option_log_syslog = TUN_FALSE; + break; + case '?': - fprintf (stderr, "Try `%s --help' for more information.\n", pgm); - exit (1); + panic ("unknown option -- %c\n" + "Try `%s --help' for more information.", optopt, getprogname ()); } if (optind != argc) - { - fprintf (stderr, "%s: too many arguments\n", pgm); - exit (1); - } + panic ("Too many arguments"); if (shutdown) { @@ -679,71 +680,76 @@ main (const int argc, char *argv[]) client_request_shutdown req; if (req.make_request () == -1 || req.error_code ()) - { - fprintf (stderr, "%s: shutdown request failed: %s\n", - pgm, strerror (req.error_code ())); - exit (1); - } + panic("Shutdown request failed: %s", strerror (req.error_code ())); // FIXME: It would be nice to wait here for the daemon to exit. return 0; } -#define SIGHANDLE(SIG) \ - do \ - { \ - struct sigaction act; \ - \ - act.sa_handler = &handle_signal; \ - act.sa_mask = 0; \ - act.sa_flags = 0; \ - \ - if (sigaction (SIG, &act, NULL) == -1) \ - { \ - system_printf ("failed to install handler for " #SIG ": %s", \ - strerror (errno)); \ - exit (1); \ - } \ - } while (false) - SIGHANDLE (SIGHUP); SIGHANDLE (SIGINT); SIGHANDLE (SIGTERM); - print_version (pgm); - setbuf (stdout, NULL); - printf ("daemon starting up"); + tunable_param_init (config_file, force_config_file); + loginit (option_log_stderr, option_log_syslog); + + log (LOG_INFO, "daemon starting up"); + + if (!cleanup_threads) + TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads); + if (!cleanup_threads) + cleanup_threads = 2; + + if (!request_threads) + TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads); + if (!request_threads) + request_threads = 10; + + if (support_sharedmem == TUN_UNDEF) + TUNABLE_BOOL_FETCH ("kern.srv.sharedmem", &support_sharedmem); + if (support_sharedmem == TUN_UNDEF) + support_sharedmem = TUN_TRUE; + + if (support_msgqueues == TUN_UNDEF) + TUNABLE_BOOL_FETCH ("kern.srv.msgqueues", &support_msgqueues); + if (support_msgqueues == TUN_UNDEF) + support_msgqueues = TUN_TRUE; + + if (support_semaphores == TUN_UNDEF) + TUNABLE_BOOL_FETCH ("kern.srv.semaphores", &support_semaphores); + if (support_semaphores == TUN_UNDEF) + support_semaphores = TUN_TRUE; + + wincap.init (); + if (wincap.has_security () && !setup_privileges ()) + panic ("Setting process privileges failed."); + + /*XXXXX*/ threaded_queue request_queue (request_threads); - printf ("."); transport_layer_base *const transport = create_server_transport (); assert (transport); - printf ("."); process_cache cache (cleanup_threads); - printf ("."); server_submission_loop submission_loop (&request_queue, transport, &cache); - printf ("."); request_queue.add_submission_loop (&submission_loop); - printf ("."); if (transport->listen () == -1) { exit (1); } - printf ("."); + + ipcinit (); cache.start (); - printf ("."); request_queue.start (); - printf ("."); - printf ("complete\n"); + log (LOG_NOTICE, "Initialization complete. Waiting for requests."); /* TODO: wait on multiple objects - the thread handle for each * request loop + all the process handles. This should be done by @@ -758,16 +764,25 @@ main (const int argc, char *argv[]) -- if signal event then retrigger it */ while (!shutdown_server && request_queue.running () && cache.running ()) - pause (); + { + pause (); + if (ipcunload ()) + { + shutdown_server = false; + log (LOG_WARNING, "Shutdown request received but ignored. " + "Dependent processes still running."); + } + } - printf ("\nShutdown request received - new requests will be denied\n"); + log (LOG_INFO, "Shutdown request received - new requests will be denied"); request_queue.stop (); - printf ("All pending requests processed\n"); - safe_delete (transport); - printf ("No longer accepting requests - cygwin will operate in daemonless mode\n"); + log (LOG_INFO, "All pending requests processed"); + delete transport; + log (LOG_INFO, "No longer accepting requests - cygwin will operate in daemonless mode"); cache.stop (); - printf ("All outstanding process-cache activities completed\n"); - printf ("daemon shutdown\n"); + log (LOG_INFO, "All outstanding process-cache activities completed"); + log (LOG_NOTICE, "Shutdown finished."); return 0; } +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/cygserver.conf b/winsup/cygserver/cygserver.conf new file mode 100644 index 000000000..b7db6bbcf --- /dev/null +++ b/winsup/cygserver/cygserver.conf @@ -0,0 +1,126 @@ +# cygserver.conf, Copyright(C) 2003 Red Hat Inc. +# +# Contains configurable parameters for the cygserver. +# +# The format of this file is easy. Lines beginning with a hash `#' are +# comments and ignored. Lines consisting of only whitespaces are ignored. +# Any other line is a setting for cygserver. +# A setting consists of a name/value pair, separated by whitespace. +# Each line must only consist of one name/value pair. +# Lines must not be longer than 1023 characters. +# +# Some settings can be overridden by a command line switch. If so, it's +# mentioned below. +# +# Settings which are commented out will use the default values. These are +# mentioned below, too. + +# kern.srv.cleanup_threads: No. of cygserver threads used for cleanup tasks. +# Default: 2, Min: 1, Max: 16, command line option -c, --cleanup-threads +#kern.srv.cleanup_threads 2 + +# kern.srv.request_threads: No. of cygserver threads used to serve +# application requests. +# Default: 10, Min: 1, Max: 64, command line option -r, --request-threads +#kern.srv.request_threads 10 + +# kern.srv.msgqueues: Determines whether XSI Message Queue support should be +# started, "yes" (or "true", "y", "t", "1") or "no" (or "false", "n", "f", "0"). +# These values are valid for all binary type options. +# Default is "yes". Command line option -q, --no-msgqueues +#kern.srv.msgqueues yes + +# kern.srv.semaphores: Determines whether XSI Semaphore support should be +# started. Default is "yes". Command line option -s, --no-semaphores +#kern.srv.semaphores yes + +# kern.srv.sharedmem: Determines whether XSI Shared Memory support should be +# started. Default is "yes". Command line option -m, --no-sharedmem +#kern.srv.sharedmem yes + +# LOGGING + +# kern.log.syslog: Determines whether logging should go to the syslog, +# Default is "yes", if stderr is no tty, "no" otherwise. +# Command line option -y, --syslog or -Y, --no-syslog. +#kern.log.syslog no + +# kern.log.stderr: Determines whether logging should go to stderr, +# Default is "yes", if stderr is a tty, "no" otherwise. +# Command line option -e, --stderr or -E, --no-stderr. +#kern.log.stderr no + +# kern.log.level: Logging level. Valid values are 1 to 7 with a bigger +# value emitting more logging output. Default level is 6. +# Command line option -l, --log-level. +#kern.log.level 6 + +# kern.log.debug: Determines whether debug output should be printed to stderr. +# Default is "no". Command line option -d, --debug +#kern.log.debug no + +# XSI message queue parameters. +# +# Each message is broken up and stored in segments that are msgssz bytes +# long. For efficiency reasons, this should be a power of two. Also, +# it doesn't make sense if it is less than 8 or greater than about 256. + +# kern.ipc.msgseg: Maximum no. of message queue segments hold concurrently. +# Default: 2048, Min: 256, Max: 32767 +#kern.ipc.msgseg 2048 + +# kern.ipc.msgssz: Size of segment in bytes. Must be a power of 2 value. +# Default: 8, Min: 8, Max: 1024 +#kern.ipc.msgssz 8 + +# kern.ipc.msgmni: Maximum no. of message queue identifiers hold concurrently. +# Default: 40, Min: 1, Max: 1024 +#kern.ipc.msgmni 40 + +# XSI semaphore parameters + +# kern.ipc.semmni: Maximum no. of semaphore identifiers hold concurrently. +# Default: 10, Min: 1, Max: 1024 +#kern.ipc.semmni 10 + +# kern.ipc.semmns: Maximum no. of semaphores hold concurrently. +# Default: 60, Min: 1, Max: 1024 +#kern.ipc.semmns 60 + +# kern.ipc.semmnu: Total no. of undo structures hold by server. +# Default: 30, Min: 1, Max: 1024 +#kern.ipc.semmnu 30 + +# kern.ipc.semmsl: Maximum no. of semaphores per semaphore id. +# Default: 60, Min: 1, Max: 1024 +#kern.ipc.semmsl 60 + +# kern.ipc.semopm: Maximum no. of operations per semop call. +# Default: 100, Min: 1, Max: 1024 +#kern.ipc.semopm 100 + +# kern.ipc.semume: Maximum no. of undo entries per process. +# Default: 10, Min: 1, Max: 1024 +#kern.ipc.semume 10 + +# kern.ipc.semvmx: Maximum value of a semaphore. +# Default: 32767, Min: 1, Max: 32767 +#kern.ipc.semvmx 32767 + +# kern.ipc.semaem: Maximum value to adjust on process exit. +# Default: 16384, Min: 1, Max: 32767 +#kern.ipc.semaem 16384 + +# XSI shared memory parameters + +# kern.ipc.shmmaxpgs: Maximum pages available for XSI shared memory. +# Default: 8192, Min: 1, Max: 32767 +#kern.ipc.shmmaxpgs 8192 + +# kern.ipc.shmmni: Maximum number of shared memory segments, system wide. +# Default: 192, Min: 1, Max: 32767 +#kern.ipc.shmmni 192 + +# kern.ipc.shmseg: Maximum number of shared memory segments per process. +# Default: 128, Min: 1, Max: 32767 +#kern.ipc.shmseg 128 diff --git a/winsup/cygserver/msg.cc b/winsup/cygserver/msg.cc index fecaa068a..713a5866a 100644 --- a/winsup/cygserver/msg.cc +++ b/winsup/cygserver/msg.cc @@ -1,8 +1,6 @@ /* msg.cc: Single unix specification IPC interface for Cygwin. - Copyright 2002 Red Hat, Inc. - - Written by Conrad Scott . + Copyright 2003 Red Hat, Inc. This file is part of Cygwin. @@ -10,38 +8,103 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#include "winsup.h" +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" -#include -#include +#include +#include +#include +#include +#include +#include +#include "cygserver.h" +#include "process.h" +#include "transport.h" -#include "cygerrno.h" +#include "cygserver_ipc.h" +#include "cygserver_msg.h" -extern "C" int -msgctl (int msqid, int cmd, struct msqid_ds *buf) -{ - set_errno (ENOSYS); - return -1; +client_request_msg::client_request_msg () + : client_request (CYGSERVER_REQUEST_MSG, + &_parameters, sizeof (_parameters)) +{ } -extern "C" int -msgget (key_t key, int msgflg) +void +client_request_msg::serve (transport_layer_base *const conn, + process_cache *const cache) { - set_errno (ENOSYS); - return -1; -} - -extern "C" ssize_t -msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) -{ - set_errno (ENOSYS); - return -1; -} - -extern "C" int -msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg) -{ - set_errno (ENOSYS); - return -1; + if (msglen () != sizeof (_parameters.in)) + { + syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + sizeof (_parameters), msglen ()); + error_code (EINVAL); + msglen (0); + return; + } + if (support_msgqueues == TUN_FALSE) + { + syscall_printf ("Message queue support not started"); + error_code (ENOSYS); + if (_parameters.in.msgop == MSGOP_msgrcv) + _parameters.out.rcv = -1; + else + _parameters.out.ret = -1; + msglen (sizeof (_parameters.out)); + return; + } + process *const client = cache->process (_parameters.in.ipcblk.cygpid, + _parameters.in.ipcblk.winpid); + if (!client) + { + error_code (EAGAIN); + msglen (0); + return; + } + if (!conn->impersonate_client ()) + { + client->release (); + error_code (EACCES); + msglen (0); + return; + } + if (!adjust_identity_info (&_parameters.in.ipcblk)) + { + conn->revert_to_self (); + error_code (EACCES); + msglen (0); + return; + } + /* Early revert_to_self since IPC code runs in kernel mode. */ + conn->revert_to_self (); + thread td = { client, &_parameters.in.ipcblk, {-1, -1} }; + int res; + msgop_t msgop = _parameters.in.msgop; /* Get's overwritten otherwise. */ + switch (msgop) + { + case MSGOP_msgctl: + res = msgctl (&td, &_parameters.in.ctlargs); + break; + case MSGOP_msgget: + res = msgget (&td, &_parameters.in.getargs); + break; + case MSGOP_msgrcv: + res = msgrcv (&td, &_parameters.in.rcvargs); + break; + case MSGOP_msgsnd: + res = msgsnd (&td, &_parameters.in.sndargs); + break; + } + /* Allocated by the call to adjust_identity_info(). */ + if (_parameters.in.ipcblk.gidlist) + free (_parameters.in.ipcblk.gidlist); + client->release (); + error_code (res); + if (msgop == MSGOP_msgrcv) + _parameters.out.rcv = td.td_retval[0]; + else + _parameters.out.ret = td.td_retval[0]; + msglen (sizeof (_parameters.out)); } +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/process.cc b/winsup/cygserver/process.cc index aa8294f57..0f6e0cb5f 100644 --- a/winsup/cygserver/process.cc +++ b/winsup/cygserver/process.cc @@ -1,4 +1,4 @@ -/* cygserver_process.cc +/* process.cc Copyright 2001, 2002 Red Hat Inc. @@ -19,7 +19,7 @@ details. */ #include "cygerrno.h" -#include "cygserver_process.h" +#include "process.h" /*****************************************************************************/ @@ -29,7 +29,7 @@ details. */ process_cleanup::~process_cleanup () { - safe_delete (_process); + delete _process; } void @@ -139,7 +139,7 @@ process::remove (const cleanup_routine *const entry) else _routines_head = ptr->_next; - safe_delete (ptr); + delete ptr; res = true; break; } @@ -170,7 +170,7 @@ process::cleanup () cleanup_routine *const ptr = entry; entry = entry->_next; ptr->cleanup (this); - safe_delete (ptr); + delete ptr; } } @@ -250,11 +250,11 @@ process_cache::process (const pid_t cygpid, const DWORD winpid) return NULL; } - entry = safe_new (class process, cygpid, winpid); + entry = new class process (cygpid, winpid); if (!entry->is_active ()) { LeaveCriticalSection (&_cache_write_access); - safe_delete (entry); + delete entry; set_errno (ESRCH); return NULL; } @@ -408,7 +408,7 @@ process_cache::check_and_remove_process (const size_t index) LeaveCriticalSection (&_cache_write_access); /* Schedule any cleanup tasks for this process. */ - _queue.add (safe_new (process_cleanup, process)); + _queue.add (new process_cleanup (process)); } class process * diff --git a/winsup/cygserver/cygserver_process.h b/winsup/cygserver/process.h similarity index 96% rename from winsup/cygserver/cygserver_process.h rename to winsup/cygserver/process.h index 25c634e9e..142b3634c 100644 --- a/winsup/cygserver/cygserver_process.h +++ b/winsup/cygserver/process.h @@ -1,4 +1,4 @@ -/* cygserver_process.h +/* process.h Copyright 2001, 2002 Red Hat Inc. @@ -10,8 +10,8 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef _CYGSERVER_PROCESS_ -#define _CYGSERVER_PROCESS_ +#ifndef _PROCESS_H +#define _PROCESS_H #include @@ -161,4 +161,4 @@ private: class process *find (DWORD winpid, class process **previous = NULL); }; -#endif /* _CYGSERVER_PROCESS_ */ +#endif /* _PROCESS_H */ diff --git a/winsup/cygserver/sem.cc b/winsup/cygserver/sem.cc index 97d91a354..6a179b0b6 100644 --- a/winsup/cygserver/sem.cc +++ b/winsup/cygserver/sem.cc @@ -1,8 +1,6 @@ /* sem.cc: Single unix specification IPC interface for Cygwin. - Copyright 2002 Red Hat, Inc. - - Written by Conrad Scott . + Copyright 2003 Red Hat, Inc. This file is part of Cygwin. @@ -10,31 +8,94 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#include "winsup.h" +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" -#include -#include +#include +#include +#include +#include +#include +#include +#include "cygserver.h" +#include "process.h" +#include "transport.h" -#include "cygerrno.h" +#include "cygserver_ipc.h" +#include "cygserver_sem.h" -extern "C" int -semctl (int semid, int semnum, int cmd, ...) -{ - set_errno (ENOSYS); - return -1; +client_request_sem::client_request_sem () + : client_request (CYGSERVER_REQUEST_SEM, + &_parameters, sizeof (_parameters)) +{ } -extern "C" int -semget (key_t key, int nsems, int semflg) +void +client_request_sem::serve (transport_layer_base *const conn, + process_cache *const cache) { - set_errno (ENOSYS); - return -1; -} - -extern "C" int -semop (int semid, struct sembuf *sops, size_t nsops) -{ - set_errno (ENOSYS); - return -1; + if (msglen () != sizeof (_parameters.in)) + { + syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + sizeof (_parameters), msglen ()); + error_code (EINVAL); + msglen (0); + return; + } + if (support_semaphores == TUN_FALSE) + { + syscall_printf ("Semaphore support not started"); + error_code (ENOSYS); + _parameters.out.ret = -1; + msglen (sizeof (_parameters.out)); + return; + } + process *const client = cache->process (_parameters.in.ipcblk.cygpid, + _parameters.in.ipcblk.winpid); + if (!client) + { + error_code (EAGAIN); + msglen (0); + return; + } + if (!conn->impersonate_client ()) + { + client->release (); + error_code (EACCES); + msglen (0); + return; + } + if (!adjust_identity_info (&_parameters.in.ipcblk)) + { + client->release (); + conn->revert_to_self (); + error_code (EACCES); + msglen (0); + return; + } + /* Early revert_to_self since IPC code runs in kernel mode. */ + conn->revert_to_self (); + thread td = { client, &_parameters.in.ipcblk, {-1, -1} }; + int res; + switch (_parameters.in.semop) + { + case SEMOP_semctl: + res = semctl (&td, &_parameters.in.ctlargs); + break; + case SEMOP_semget: + res = semget (&td, &_parameters.in.getargs); + break; + case SEMOP_semop: + res = semop (&td, &_parameters.in.opargs); + break; + } + /* Allocated by the call to adjust_identity_info(). */ + if (_parameters.in.ipcblk.gidlist) + free (_parameters.in.ipcblk.gidlist); + client->release (); + error_code (res); + _parameters.out.ret = td.td_retval[0]; + msglen (sizeof (_parameters.out)); } +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/shm.cc b/winsup/cygserver/shm.cc index 50d2b6e1d..f73fd012a 100644 --- a/winsup/cygserver/shm.cc +++ b/winsup/cygserver/shm.cc @@ -1,9 +1,6 @@ -/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin. +/* shm.cc: Single unix specification IPC interface for Cygwin. - Copyright 2002 Red Hat, Inc. - - Written by Conrad Scott . - Based on code by Robert Collins . + Copyright 2003 Red Hat, Inc. This file is part of Cygwin. @@ -11,824 +8,33 @@ 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 #include #include +#include #include #include +#include "cygserver.h" +#include "process.h" +#include "transport.h" + #include "cygserver_ipc.h" #include "cygserver_shm.h" -#include "security.h" - -#include "cygserver.h" -#include "cygserver_process.h" -#include "cygserver_transport.h" - -/*---------------------------------------------------------------------------* - * class server_shmmgr - * - * A singleton class. - *---------------------------------------------------------------------------*/ - -#define shmmgr (server_shmmgr::instance ()) - -class server_shmmgr -{ -private: - class attach_t - { - public: - class process *const _client; - unsigned int _refcnt; - - attach_t *_next; - - attach_t (class process *const client) - : _client (client), - _refcnt (0), - _next (NULL) - {} - }; - - class segment_t - { - private: - // Bits for the _flg field. - enum { IS_DELETED = 0x01 }; - - public: - const int _intid; - const int _shmid; - struct shmid_ds _ds; - - segment_t *_next; - - segment_t (const key_t key, const int intid, const HANDLE hFileMap); - ~segment_t (); - - bool is_deleted () const - { - return _flg & IS_DELETED; - } - - bool is_pending_delete () const - { - return !_ds.shm_nattch && is_deleted (); - } - - void mark_deleted () - { - assert (!is_deleted ()); - - _flg |= IS_DELETED; - } - - int attach (class process *, HANDLE & hFileMap); - int detach (class process *); - - private: - static long _sequence; - - int _flg; - const HANDLE _hFileMap; - attach_t *_attach_head; // A list sorted by winpid; - - attach_t *find (const class process *, attach_t **previous = NULL); - }; - - class cleanup_t : public cleanup_routine - { - public: - cleanup_t (const segment_t *const segptr) - : cleanup_routine (reinterpret_cast (segptr->_shmid)) - { - assert (key ()); - } - - int shmid () const { return reinterpret_cast (key ()); } - - virtual void cleanup (class process *const client) - { - const int res = shmmgr.shmdt (shmid (), client); - - if (res != 0) - debug_printf ("process cleanup failed [shmid = %d]: %s", - shmid (), strerror (-res)); - } - }; - -public: - static server_shmmgr & instance (); - - int shmat (HANDLE & hFileMap, - int shmid, int shmflg, class process *); - int shmctl (int & out_shmid, struct shmid_ds & out_ds, - struct shminfo & out_shminfo, struct shm_info & out_shm_info, - const int shmid, int cmd, const struct shmid_ds &, - class process *); - int shmdt (int shmid, class process *); - int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t, - class process *); - -private: - static server_shmmgr *_instance; - static pthread_once_t _instance_once; - - static void initialise_instance (); - - CRITICAL_SECTION _segments_lock; - segment_t *_segments_head; // A list sorted by int_id. - - int _shm_ids; // Number of shm segments (for ipcs(8)). - int _shm_tot; // Total bytes of shm segments (for ipcs(8)). - int _shm_atts; // Number of attached segments (for ipcs(8)). - int _intid_max; // Highest intid yet allocated (for ipcs(8)). - - server_shmmgr (); - ~server_shmmgr (); - - // Undefined (as this class is a singleton): - server_shmmgr (const server_shmmgr &); - server_shmmgr & operator= (const server_shmmgr &); - - segment_t *find_by_key (key_t); - segment_t *find (int intid, segment_t **previous = NULL); - - int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t); - - segment_t *new_segment (key_t, size_t, HANDLE); - void delete_segment (segment_t *); -}; - -/* static */ long server_shmmgr::segment_t::_sequence = 0; - -/* static */ server_shmmgr *server_shmmgr::_instance = NULL; -/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT; - -/*---------------------------------------------------------------------------* - * server_shmmgr::segment_t::segment_t () - *---------------------------------------------------------------------------*/ - -server_shmmgr::segment_t::segment_t (const key_t key, - const int intid, - const HANDLE hFileMap) - : _intid (intid), - _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)), - _next (NULL), - _flg (0), - _hFileMap (hFileMap), - _attach_head (NULL) -{ - assert (0 <= _intid && _intid < SHMMNI); - - memset (&_ds, '\0', sizeof (_ds)); - _ds.shm_perm.key = key; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::segment_t::~segment_t () - *---------------------------------------------------------------------------*/ - -server_shmmgr::segment_t::~segment_t () -{ - assert (!_attach_head); - - if (!CloseHandle (_hFileMap)) - syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap); -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::segment_t::attach () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::segment_t::attach (class process *const client, - HANDLE & hFileMap) -{ - assert (client); - - if (!DuplicateHandle (GetCurrentProcess (), - _hFileMap, - client->handle (), - &hFileMap, - 0, - FALSE, // bInheritHandle - DUPLICATE_SAME_ACCESS)) - { - syscall_printf (("failed to duplicate handle for client " - "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"), - _ds.shm_perm.key, _shmid, _hFileMap); - - return -EACCES; // FIXME: Case analysis? - } - - _ds.shm_lpid = client->cygpid (); - _ds.shm_nattch += 1; - _ds.shm_atime = time (NULL); // FIXME: sub-second times. - - attach_t *previous = NULL; - attach_t *attptr = find (client, &previous); - - if (!attptr) - { - attptr = safe_new (attach_t, client); - - if (previous) - { - attptr->_next = previous->_next; - previous->_next = attptr; - } - else - { - attptr->_next = _attach_head; - _attach_head = attptr; - } - } - - attptr->_refcnt += 1; - - cleanup_t *const cleanup = safe_new (cleanup_t, this); - - // FIXME: ::add should only fail if the process object is already - // cleaning up; but it can't be doing that since this thread has it - // locked. - - const bool result = client->add (cleanup); - - assert (result); - - return 0; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::segment_t::detach () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::segment_t::detach (class process *const client) -{ - attach_t *previous = NULL; - attach_t *const attptr = find (client, &previous); - - if (!attptr) - return -EINVAL; - - if (client->is_active ()) - { - const cleanup_t key (this); - - if (!client->remove (&key)) - syscall_printf (("failed to remove cleanup routine for %d(%lu) " - "[shmid = %d]"), - client->cygpid (), client->winpid (), - _shmid); - } - - attptr->_refcnt -= 1; - - if (!attptr->_refcnt) - { - assert (previous ? previous->_next == attptr : _attach_head == attptr); - - if (previous) - previous->_next = attptr->_next; - else - _attach_head = attptr->_next; - - safe_delete (attptr); - } - - assert (_ds.shm_nattch > 0); - - _ds.shm_lpid = client->cygpid (); - _ds.shm_nattch -= 1; - _ds.shm_dtime = time (NULL); // FIXME: sub-second times. - - return 0; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::segment_t::find () - *---------------------------------------------------------------------------*/ - -server_shmmgr::attach_t * -server_shmmgr::segment_t::find (const class process *const client, - attach_t **previous) -{ - if (previous) - *previous = NULL; - - // Nb. The _attach_head list is sorted by winpid. - - for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next) - if (attptr->_client == client) - return attptr; - else if (attptr->_client->winpid () > client->winpid ()) - return NULL; - else if (previous) - *previous = attptr; - - return NULL; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::instance () - *---------------------------------------------------------------------------*/ - -/* static */ server_shmmgr & -server_shmmgr::instance () -{ - pthread_once (&_instance_once, &initialise_instance); - - assert (_instance); - - return *_instance; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::shmat () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::shmat (HANDLE & hFileMap, - const int shmid, const int shmflg, - class process *const client) -{ - syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)", - shmid, shmflg, client->cygpid (), client->winpid ()); - - int result = 0; - EnterCriticalSection (&_segments_lock); - - segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP)); - - if (!segptr) - result = -EINVAL; - else - result = segptr->attach (client, hFileMap); - - if (!result) - _shm_atts += 1; - - LeaveCriticalSection (&_segments_lock); - - if (result < 0) - syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) " - "for %d(%lu)"), - -result, shmid, shmflg, - client->cygpid (), client->winpid ()); - else - syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) " - "for %d(%lu)"), - hFileMap, shmid, shmflg, - client->cygpid (), client->winpid ()); - - return result; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::shmctl () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::shmctl (int & out_shmid, - struct shmid_ds & out_ds, - struct shminfo & out_shminfo, - struct shm_info & out_shm_info, - const int shmid, const int cmd, - const struct shmid_ds & ds, - class process *const client) -{ - syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)", - shmid, cmd, client->cygpid (), client->winpid ()); - - int result = 0; - EnterCriticalSection (&_segments_lock); - - switch (cmd) - { - case IPC_STAT: - case SHM_STAT: // Uses intids rather than shmids. - case IPC_SET: - case IPC_RMID: - { - int intid; - - if (cmd == SHM_STAT) - intid = shmid; - else - intid = ipc_ext2int (shmid, IPC_SHMOP); - - segment_t *const segptr = find (intid); - - if (!segptr) - result = -EINVAL; - else - switch (cmd) - { - case IPC_STAT: - out_ds = segptr->_ds; - break; - - case IPC_SET: - segptr->_ds.shm_perm.uid = ds.shm_perm.uid; - segptr->_ds.shm_perm.gid = ds.shm_perm.gid; - segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777; - segptr->_ds.shm_lpid = client->cygpid (); - segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times. - break; - - case IPC_RMID: - if (segptr->is_deleted ()) - result = -EIDRM; - else - { - segptr->mark_deleted (); - if (segptr->is_pending_delete ()) - delete_segment (segptr); - } - break; - - case SHM_STAT: // ipcs(8) i'face. - out_ds = segptr->_ds; - out_shmid = segptr->_shmid; - break; - } - } - break; - - case IPC_INFO: - out_shminfo.shmmax = SHMMAX; - out_shminfo.shmmin = SHMMIN; - out_shminfo.shmmni = SHMMNI; - out_shminfo.shmseg = SHMSEG; - out_shminfo.shmall = SHMALL; - break; - - case SHM_INFO: // ipcs(8) i'face. - out_shmid = _intid_max; - out_shm_info.shm_ids = _shm_ids; - out_shm_info.shm_tot = _shm_tot; - out_shm_info.shm_atts = _shm_atts; - break; - - default: - result = -EINVAL; - break; - } - - LeaveCriticalSection (&_segments_lock); - - if (result < 0) - syscall_printf (("-1 [%d] = " - "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"), - -result, - shmid, cmd, client->cygpid (), client->winpid ()); - else - syscall_printf (("%d = " - "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"), - ((cmd == SHM_STAT || cmd == SHM_INFO) - ? out_shmid - : result), - shmid, cmd, client->cygpid (), client->winpid ()); - - return result; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::shmdt () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::shmdt (const int shmid, class process *const client) -{ - syscall_printf ("shmdt (shmid = %d) for %d(%lu)", - shmid, client->cygpid (), client->winpid ()); - - int result = 0; - EnterCriticalSection (&_segments_lock); - - segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP)); - - if (!segptr) - result = -EINVAL; - else - result = segptr->detach (client); - - if (!result) - _shm_atts -= 1; - - if (!result && segptr->is_pending_delete ()) - delete_segment (segptr); - - LeaveCriticalSection (&_segments_lock); - - if (result < 0) - syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)", - -result, shmid, client->cygpid (), client->winpid ()); - else - syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)", - result, shmid, client->cygpid (), client->winpid ()); - - return result; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::shmget () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::shmget (int & out_shmid, - const key_t key, const size_t size, const int shmflg, - const uid_t uid, const gid_t gid, - class process *const client) -{ - syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " - "for %d(%lu)"), - key, size, shmflg, - client->cygpid (), client->winpid ()); - - int result = 0; - EnterCriticalSection (&_segments_lock); - - if (key == IPC_PRIVATE) - result = new_segment (key, size, shmflg, - client->cygpid (), uid, gid); - else - { - segment_t *const segptr = find_by_key (key); - - if (!segptr) - if (shmflg & IPC_CREAT) - result = new_segment (key, size, shmflg, - client->cygpid (), uid, gid); - else - result = -ENOENT; - else if (segptr->is_deleted ()) - result = -EIDRM; - else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) - result = -EEXIST; - else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777) - result = -EACCES; - else if (size && segptr->_ds.shm_segsz < size) - result = -EINVAL; - else - result = segptr->_shmid; - } - - LeaveCriticalSection (&_segments_lock); - - if (result >= 0) - { - out_shmid = result; - result = 0; - } - - if (result < 0) - syscall_printf (("-1 [%d] = " - "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " - "for %d(%lu)"), - -result, - key, size, shmflg, - client->cygpid (), client->winpid ()); - else - syscall_printf (("%d = " - "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " - "for %d(%lu)"), - out_shmid, - key, size, shmflg, - client->cygpid (), client->winpid ()); - - return result; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::initialise_instance () - *---------------------------------------------------------------------------*/ - -/* static */ void -server_shmmgr::initialise_instance () -{ - assert (!_instance); - - _instance = safe_new0 (server_shmmgr); - - assert (_instance); -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::server_shmmgr () - *---------------------------------------------------------------------------*/ - -server_shmmgr::server_shmmgr () - : _segments_head (NULL), - _shm_ids (0), - _shm_tot (0), - _shm_atts (0), - _intid_max (0) -{ - InitializeCriticalSection (&_segments_lock); -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::~server_shmmgr () - *---------------------------------------------------------------------------*/ - -server_shmmgr::~server_shmmgr () -{ - DeleteCriticalSection (&_segments_lock); -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::find_by_key () - *---------------------------------------------------------------------------*/ - -server_shmmgr::segment_t * -server_shmmgr::find_by_key (const key_t key) -{ - for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next) - if (segptr->_ds.shm_perm.key == key) - return segptr; - - return NULL; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::find () - *---------------------------------------------------------------------------*/ - -server_shmmgr::segment_t * -server_shmmgr::find (const int intid, segment_t **previous) -{ - if (previous) - *previous = NULL; - - for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next) - if (segptr->_intid == intid) - return segptr; - else if (segptr->_intid > intid) // The list is sorted by intid. - return NULL; - else if (previous) - *previous = segptr; - - return NULL; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::new_segment () - *---------------------------------------------------------------------------*/ - -int -server_shmmgr::new_segment (const key_t key, - const size_t size, - const int shmflg, - const pid_t cygpid, - const uid_t uid, - const gid_t gid) -{ - if (size < SHMMIN || size > SHMMAX) - return -EINVAL; - - const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE, - NULL, PAGE_READWRITE, - 0, size, - NULL); - - if (!hFileMap) - { - syscall_printf ("failed to create file mapping [size = %lu]: %E", size); - return -ENOMEM; // FIXME - } - - segment_t *const segptr = new_segment (key, size, hFileMap); - - if (!segptr) - { - (void) CloseHandle (hFileMap); - return -ENOSPC; - } - - segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid; - segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid; - segptr->_ds.shm_perm.mode = shmflg & 0777; - segptr->_ds.shm_segsz = size; - segptr->_ds.shm_cpid = cygpid; - segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times. - - return segptr->_shmid; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::new_segment () - * - * Allocate a new segment for the given key and file map with the - * lowest available intid and insert into the segment map. - *---------------------------------------------------------------------------*/ - -server_shmmgr::segment_t * -server_shmmgr::new_segment (const key_t key, const size_t size, - const HANDLE hFileMap) -{ - // FIXME: Overflow risk. - if (_shm_tot + size > SHMALL) - return NULL; - - int intid = 0; // Next expected intid value. - segment_t *previous = NULL; // Insert pointer. - - // Find first unallocated intid. - for (segment_t *segptr = _segments_head; - segptr && segptr->_intid == intid; - segptr = segptr->_next, intid++) - { - previous = segptr; - } - - /* By the time this condition is reached (given the default value of - * SHMMNI), the linear searches should all replaced by something - * just a *little* cleverer . . . - */ - if (intid >= SHMMNI) - return NULL; - - segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap); - - assert (segptr); - - if (previous) - { - segptr->_next = previous->_next; - previous->_next = segptr; - } - else - { - segptr->_next = _segments_head; - _segments_head = segptr; - } - - _shm_ids += 1; - _shm_tot += size; - if (intid > _intid_max) - _intid_max = intid; - - return segptr; -} - -/*---------------------------------------------------------------------------* - * server_shmmgr::delete_segment () - *---------------------------------------------------------------------------*/ - -void -server_shmmgr::delete_segment (segment_t *const segptr) -{ - assert (segptr); - assert (segptr->is_pending_delete ()); - - segment_t *previous = NULL; - - const segment_t *const tmp = find (segptr->_intid, &previous); - - assert (tmp == segptr); - assert (previous ? previous->_next == segptr : _segments_head == segptr); - - if (previous) - previous->_next = segptr->_next; - else - _segments_head = segptr->_next; - - assert (_shm_ids > 0); - _shm_ids -= 1; - _shm_tot -= segptr->_ds.shm_segsz; - - safe_delete (segptr); -} - -/*---------------------------------------------------------------------------* - * client_request_shm::client_request_shm () - *---------------------------------------------------------------------------*/ client_request_shm::client_request_shm () : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) -{ - // verbose: syscall_printf ("created"); +{ } -/*---------------------------------------------------------------------------* - * client_request_shm::serve () - *---------------------------------------------------------------------------*/ - void client_request_shm::serve (transport_layer_base *const conn, - process_cache *const cache) + process_cache *const cache) { - assert (conn); - - assert (!error_code ()); - if (msglen () != sizeof (_parameters.in)) { syscall_printf ("bad request body length: expecting %lu bytes, got %lu", @@ -837,60 +43,76 @@ client_request_shm::serve (transport_layer_base *const conn, msglen (0); return; } - - // FIXME: Get a return code out of this and don't continue on error. - conn->impersonate_client (); - - class process *const client = cache->process (_parameters.in.cygpid, - _parameters.in.winpid); - + if (support_sharedmem == TUN_FALSE) + { + syscall_printf ("Shared memory support not started"); + error_code (ENOSYS); + if (_parameters.in.shmop == SHMOP_shmat) + _parameters.out.ptr = (vm_offset_t)0; + else + _parameters.out.ret = -1; + msglen (sizeof (_parameters.out)); + return; + } + process *const client = cache->process (_parameters.in.ipcblk.cygpid, + _parameters.in.ipcblk.winpid); if (!client) { error_code (EAGAIN); msglen (0); return; } - - int result = -EINVAL; - - switch (_parameters.in.shmop) + if (!conn->impersonate_client ()) { - case SHMOP_shmget: - result = shmmgr.shmget (_parameters.out.shmid, - _parameters.in.key, _parameters.in.size, - _parameters.in.shmflg, - _parameters.in.uid, _parameters.in.gid, - client); - break; - - case SHMOP_shmat: - result = shmmgr.shmat (_parameters.out.hFileMap, - _parameters.in.shmid, _parameters.in.shmflg, - client); - break; - - case SHMOP_shmdt: - result = shmmgr.shmdt (_parameters.in.shmid, client); - break; - - case SHMOP_shmctl: - result = shmmgr.shmctl (_parameters.out.shmid, - _parameters.out.ds, _parameters.out.shminfo, - _parameters.out.shm_info, - _parameters.in.shmid, _parameters.in.cmd, - _parameters.in.ds, - client); - break; - } - - client->release (); - conn->revert_to_self (); - - if (result < 0) - { - error_code (-result); + client->release (); + error_code (EACCES); msglen (0); + return; } + if (!adjust_identity_info (&_parameters.in.ipcblk)) + { + client->release (); + conn->revert_to_self (); + error_code (EACCES); + msglen (0); + return; + } + /* Early revert_to_self since IPC code runs in kernel mode. */ + conn->revert_to_self (); + thread td = { client, &_parameters.in.ipcblk, {0, 0} }; + int res; + shmop_t shmop = _parameters.in.shmop; /* Get's overwritten otherwise. */ + switch (shmop) + { + case SHMOP_shmat: + ipc_p_vmspace (td.ipcblk); + res = shmat (&td, &_parameters.in.atargs); + break; + case SHMOP_shmctl: + res = shmctl (&td, &_parameters.in.ctlargs); + break; + case SHMOP_shmdt: + ipc_p_vmspace (td.ipcblk); + res = shmdt (&td, &_parameters.in.dtargs); + break; + case SHMOP_shmget: + res = shmget (&td, &_parameters.in.getargs); + break; + case SHMOP_shmfork: + res = cygwin_shmfork_myhook (&td, &_parameters.in.forkargs); + break; + } + /* Allocated by the call to adjust_identity_info(). */ + if (_parameters.in.ipcblk.gidlist) + free (_parameters.in.ipcblk.gidlist); + client->release (); + error_code (res); + if (shmop == SHMOP_shmat) + _parameters.out.ptr = td.td_retval[0]; else - msglen (sizeof (_parameters.out)); + _parameters.out.ret = td.td_retval[0]; + if (shmop == SHMOP_shmget) + _parameters.out.obj = td.td_retval[1]; + msglen (sizeof (_parameters.out)); } +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/sysv_msg.cc b/winsup/cygserver/sysv_msg.cc new file mode 100644 index 000000000..9c1049c97 --- /dev/null +++ b/winsup/cygserver/sysv_msg.cc @@ -0,0 +1,1208 @@ +/* + * Implementation of SVID messages + * + * Author: Daniel Boulet + * + * Copyright 1993 Daniel Boulet and RTMX Inc. + * + * This system call was implemented by Daniel Boulet under contract from RTMX. + * + * Redistribution and use in source forms, with and without modification, + * are permitted provided that this entire comment appears intact. + * + * Redistribution in binary form may occur without any restrictions. + * Obviously, it would be nice if you gave credit where credit is due + * but requiring it would be too onerous. + * + * This software is provided ``AS IS'' without any warranties of any kind. + */ + +/* + * This file is heavily changed to become part of Cygwin's cygserver. + */ + +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#include +#ifndef __FBSDID +#define __FBSDID(s) const char version[] = (s) +#endif +__FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/sysv_msg.c,v 1.52 2003/11/07 04:47:14 rwatson Exp $"); + +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include "cygserver.h" +#include "process.h" +#include "cygserver_ipc.h" + +#ifdef __CYGWIN__ +#define MSG_DEBUG +#endif /* __CYGWIN__ */ + +#ifdef MSG_DEBUG +#define DPRINTF(a) debug_printf a +#else +#define DPRINTF(a) +#endif + +static void msg_freehdr(struct msg *msghdr); + +#ifndef __CYGWIN__ +int msgctl(struct thread *, struct msgctl_args *); +int msgget(struct thread *, struct msgget_args *); +int msgsnd(struct thread *, struct msgsnd_args *); +int msgrcv(struct thread *, struct msgrcv_args *); + +static sy_call_t *msgcalls[] = { + (sy_call_t *)msgctl, (sy_call_t *)msgget, + (sy_call_t *)msgsnd, (sy_call_t *)msgrcv +}; +#endif /* __CYGWIN__ */ + + +struct msg { + struct msg *msg_next; /* next msg in the chain */ + long msg_type; /* type of this message */ + /* >0 -> type of this message */ + /* 0 -> free header */ + u_short msg_ts; /* size of this message */ + short msg_spot; /* location of start of msg in buffer */ +}; + + +#ifndef MSGSSZ +#define MSGSSZ 8 /* Each segment must be 2^N long */ +#endif +#ifndef MSGSEG +#define MSGSEG 2048 /* must be less than 32767 */ +#endif +#define MSGMAX (MSGSSZ*MSGSEG) +#ifndef MSGMNB +#define MSGMNB 2048 /* max # of bytes in a queue */ +#endif +#ifndef MSGMNI +#define MSGMNI 40 +#endif +#ifndef MSGTQL +#define MSGTQL 40 +#endif + +/* + * Based on the configuration parameters described in an SVR2 (yes, two) + * config(1m) man page. + * + * Each message is broken up and stored in segments that are msgssz bytes + * long. For efficiency reasons, this should be a power of two. Also, + * it doesn't make sense if it is less than 8 or greater than about 256. + * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of + * two between 8 and 1024 inclusive (and panic's if it isn't). + */ +struct msginfo msginfo = { + MSGMAX, /* max chars in a message */ + MSGMNB, /* max chars in a queue */ + MSGMNI, /* # of message queue identifiers */ + MSGTQL, /* max messages in system */ + MSGSSZ, /* size of a message segment */ + /* (must be small power of 2 greater than 4) */ + MSGSEG /* number of message segments */ +}; + +/* + * macros to convert between msqid_ds's and msqid's. + * (specific to this implementation) + */ +#define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000)) +#define MSQID_IX(id) ((id) & 0xffff) +#define MSQID_SEQ(id) (((id) >> 16) & 0xffff) + +/* + * The rest of this file is specific to this particular implementation. + */ + +struct msgmap { + short next; /* next segment in buffer */ + /* -1 -> available */ + /* 0..(MSGSEG-1) -> index of next segment */ +}; + +#define MSG_LOCKED 01000 /* Is this msqid_ds locked? */ + +static int nfree_msgmaps; /* # of free map entries */ +static short free_msgmaps; /* head of linked list of free map entries */ +static struct msg *free_msghdrs;/* list of free msg headers */ +static char *msgpool; /* MSGMAX byte long msg buffer pool */ +static struct msgmap *msgmaps; /* MSGSEG msgmap structures */ +static struct msg *msghdrs; /* MSGTQL msg headers */ +static struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ +static struct mtx msq_mtx; /* global mutex for message queues. */ + +#ifdef __CYGWIN__ +static struct msg_info msg_info; +#endif /* __CYGWIN__ */ + +void +msginit() +{ + int i; + + TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg); + TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz); + msginfo.msgmax = msginfo.msgseg * msginfo.msgssz; + TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni); + + msgpool = (char *) sys_malloc(msginfo.msgmax, M_MSG, M_WAITOK); + if (msgpool == NULL) + panic("msgpool is NULL"); + msgmaps = (msgmap *) sys_malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK); + if (msgmaps == NULL) + panic("msgmaps is NULL"); + msghdrs = (msg *) sys_malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK); + if (msghdrs == NULL) + panic("msghdrs is NULL"); + msqids = (msqid_ds *) sys_malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK); + if (msqids == NULL) + panic("msqids is NULL"); + + /* + * msginfo.msgssz should be a power of two for efficiency reasons. + * It is also pretty silly if msginfo.msgssz is less than 8 + * or greater than about 256 so ... + */ + + i = 8; + while (i < 1024 && i != msginfo.msgssz) + i <<= 1; + if (i != msginfo.msgssz) { + DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, + msginfo.msgssz)); + panic("msginfo.msgssz not a small power of 2"); + } + + if (msginfo.msgseg > 32767) { + DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg)); + panic("msginfo.msgseg > 32767"); + } + + if (msgmaps == NULL) + panic("msgmaps is NULL"); + + for (i = 0; i < msginfo.msgseg; i++) { + if (i > 0) + msgmaps[i-1].next = i; + msgmaps[i].next = -1; /* implies entry is available */ + } + free_msgmaps = 0; + nfree_msgmaps = msginfo.msgseg; + + if (msghdrs == NULL) + panic("msghdrs is NULL"); + + for (i = 0; i < msginfo.msgtql; i++) { + msghdrs[i].msg_type = 0; + if (i > 0) + msghdrs[i-1].msg_next = &msghdrs[i]; + msghdrs[i].msg_next = NULL; + } + free_msghdrs = &msghdrs[0]; + + if (msqids == NULL) + panic("msqids is NULL"); + + for (i = 0; i < msginfo.msgmni; i++) { + msqids[i].msg_qbytes = 0; /* implies entry is available */ + msqids[i].msg_perm.seq = 0; /* reset to a known value */ + msqids[i].msg_perm.mode = 0; + } + mtx_init(&msq_mtx, "msq", NULL, MTX_DEF); +} + +int +msgunload() +{ + struct msqid_ds *msqptr; + int msqid; + + for (msqid = 0; msqid < msginfo.msgmni; msqid++) { + /* + * Look for an unallocated and unlocked msqid_ds. + * msqid_ds's can be locked by msgsnd or msgrcv while + * they are copying the message in/out. We can't + * re-use the entry until they release it. + */ + msqptr = &msqids[msqid]; + if (msqptr->msg_qbytes != 0 || + (msqptr->msg_perm.mode & MSG_LOCKED) != 0) + break; + } +#ifndef __CYGWIN__ + if (msqid != msginfo.msgmni) + return (EBUSY); +#endif /* __CYGWIN__ */ + + sys_free(msgpool, M_MSG); + sys_free(msgmaps, M_MSG); + sys_free(msghdrs, M_MSG); + sys_free(msqids, M_MSG); + mtx_destroy(&msq_mtx); + return (0); +} + + +#ifndef __CYGWIN__ +static int +sysvmsg_modload(struct module *module, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + msginit(); + break; + case MOD_UNLOAD: + error = msgunload(); + break; + case MOD_SHUTDOWN: + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static moduledata_t sysvmsg_mod = { + "sysvmsg", + &sysvmsg_modload, + NULL +}; + +SYSCALL_MODULE_HELPER(msgsys); +SYSCALL_MODULE_HELPER(msgctl); +SYSCALL_MODULE_HELPER(msgget); +SYSCALL_MODULE_HELPER(msgsnd); +SYSCALL_MODULE_HELPER(msgrcv); + +DECLARE_MODULE(sysvmsg, sysvmsg_mod, + SI_SUB_SYSV_MSG, SI_ORDER_FIRST); +MODULE_VERSION(sysvmsg, 1); + +/* + * Entry point for all MSG calls + * + * MPSAFE + */ +int +msgsys(thread *td, struct msgsys_args *uap) +{ + int error; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + if (uap->which < 0 || + (unsigned) uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) + return (EINVAL); + error = (*msgcalls[uap->which])(td, &uap->a2); + return (error); +} +#endif + +static void +msg_freehdr(struct msg *msghdr) +{ + while (msghdr->msg_ts > 0) { + short next; + if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) + panic("msghdr->msg_spot out of range"); + next = msgmaps[msghdr->msg_spot].next; + msgmaps[msghdr->msg_spot].next = free_msgmaps; + free_msgmaps = msghdr->msg_spot; + nfree_msgmaps++; + msghdr->msg_spot = next; + if (msghdr->msg_ts >= msginfo.msgssz) + msghdr->msg_ts -= msginfo.msgssz; + else + msghdr->msg_ts = 0; + } + if (msghdr->msg_spot != -1) + panic("msghdr->msg_spot != -1"); + msghdr->msg_next = free_msghdrs; + free_msghdrs = msghdr; +} + +#ifndef _SYS_SYSPROTO_H_ +struct msgctl_args { + int msqid; + int cmd; + struct msqid_ds *buf; +}; +#endif + +/* + * MPSAFE + */ +int +msgctl(struct thread *td, struct msgctl_args *uap) +{ + int msqid = uap->msqid; + int cmd = uap->cmd; + struct msqid_ds *user_msqptr = uap->buf; + int rval, error; + struct msqid_ds msqbuf; + register struct msqid_ds *msqptr; + + DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr)); + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + +#ifdef __CYGWIN__ + if (cmd == IPC_INFO) { + if (!msqid) { + error = copyout(&msginfo, user_msqptr, + sizeof(struct msginfo)); + td->td_retval[0] = error ? -1 : 0; + return (error); + } + if (msqid > msginfo.msgmni) + msqid = msginfo.msgmni; + error = copyout(msqids, user_msqptr, + msqid * sizeof(struct msqid_ds)); + td->td_retval[0] = error ? -1 : 0; + return (error); + } else if (cmd == MSG_INFO) { + mtx_lock(&msq_mtx); + error = copyout(&msg_info, user_msqptr, + sizeof(struct msg_info)); + td->td_retval[0] = error ? -1 : 0; + mtx_unlock(&msq_mtx); + return (error); + } +#endif /* __CYGWIN__ */ + msqid = IPCID_TO_IX(msqid); + + if (msqid < 0 || msqid >= msginfo.msgmni) { + DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, + msginfo.msgmni)); + return (EINVAL); + } + if (cmd == IPC_SET && + (error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) + return (error); + + msqptr = &msqids[msqid]; + + mtx_lock(&msq_mtx); + if (msqptr->msg_qbytes == 0) { + DPRINTF(("no such msqid\n")); + error = EINVAL; + goto done2; + } + if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { + DPRINTF(("wrong sequence number\n")); + error = EINVAL; + goto done2; + } + + error = 0; + rval = 0; + + switch (cmd) { + + case IPC_RMID: + { + struct msg *msghdr; + if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) + goto done2; + /* Free the message headers */ + msghdr = msqptr->msg_first; + while (msghdr != NULL) { + struct msg *msghdr_tmp; + + /* Free the segments of each message */ + msqptr->msg_cbytes -= msghdr->msg_ts; + msqptr->msg_qnum--; + msghdr_tmp = msghdr; + msghdr = msghdr->msg_next; + msg_freehdr(msghdr_tmp); + } + + if (msqptr->msg_cbytes != 0) + panic("msg_cbytes is screwed up"); + if (msqptr->msg_qnum != 0) + panic("msg_qnum is screwed up"); + + msqptr->msg_qbytes = 0; /* Mark it as free */ +#ifdef __CYGWIN__ + msg_info.msg_ids--; +#endif /* __CYGWIN__ */ + + wakeup(msqptr); + } + + break; + + case IPC_SET: + if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) + goto done2; + if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { + error = suser(td); + if (error) + goto done2; + } + if (msqbuf.msg_qbytes > (unsigned long) msginfo.msgmnb) { + DPRINTF(("can't increase msg_qbytes beyond %d" + "(truncating)\n", msginfo.msgmnb)); + msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ + } + if (msqbuf.msg_qbytes == 0) { + DPRINTF(("can't reduce msg_qbytes to 0\n")); + error = EINVAL; /* non-standard errno! */ + goto done2; + } + msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ + msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ + msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | + (msqbuf.msg_perm.mode & 0777); + msqptr->msg_qbytes = msqbuf.msg_qbytes; + msqptr->msg_ctime = time (NULL); + break; + + case IPC_STAT: + if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { + DPRINTF(("requester doesn't have read access\n")); + goto done2; + } + break; + + default: + DPRINTF(("invalid command %d\n", cmd)); + error = EINVAL; + goto done2; + } + + if (error == 0) + td->td_retval[0] = rval; +done2: + mtx_unlock(&msq_mtx); + if (cmd == IPC_STAT && error == 0) + error = copyout(msqptr, user_msqptr, sizeof(struct msqid_ds)); + return(error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct msgget_args { + key_t key; + int msgflg; +}; +#endif + +/* + * MPSAFE + */ +int +msgget(struct thread *td, struct msgget_args *uap) +{ + int msqid, error = 0; + key_t key = uap->key; + unsigned msgflg = uap->msgflg; + register struct msqid_ds *msqptr = NULL; + + DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + mtx_lock(&msq_mtx); + if (key != IPC_PRIVATE) { + for (msqid = 0; msqid < msginfo.msgmni; msqid++) { + msqptr = &msqids[msqid]; + if (msqptr->msg_qbytes != 0 && + msqptr->msg_perm.key == key) + break; + } + if (msqid < msginfo.msgmni) { + DPRINTF(("found public key\n")); + if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { + DPRINTF(("not exclusive\n")); + error = EEXIST; + goto done2; + } + if ((error = ipcperm(td, &msqptr->msg_perm, msgflg & 0700))) { + DPRINTF(("requester doesn't have 0%o access\n", + msgflg & 0700)); + goto done2; + } + goto found; + } + } + + DPRINTF(("need to allocate the msqid_ds\n")); + if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { + for (msqid = 0; msqid < msginfo.msgmni; msqid++) { + /* + * Look for an unallocated and unlocked msqid_ds. + * msqid_ds's can be locked by msgsnd or msgrcv while + * they are copying the message in/out. We can't + * re-use the entry until they release it. + */ + msqptr = &msqids[msqid]; + if (msqptr->msg_qbytes == 0 && + (msqptr->msg_perm.mode & MSG_LOCKED) == 0) + break; + } + if (msqid == msginfo.msgmni) { + DPRINTF(("no more msqid_ds's available\n")); + error = ENOSPC; + goto done2; + } + DPRINTF(("msqid %d is available\n", msqid)); + msqptr->msg_perm.key = key; +#ifdef __CYGWIN__ + msqptr->msg_perm.cuid = td->ipcblk->uid; + msqptr->msg_perm.uid = td->ipcblk->uid; + msqptr->msg_perm.cgid = td->ipcblk->gid; + msqptr->msg_perm.gid = td->ipcblk->gid; +#else + msqptr->msg_perm.cuid = cred->cr_uid; + msqptr->msg_perm.uid = cred->cr_uid; + msqptr->msg_perm.cgid = cred->cr_gid; + msqptr->msg_perm.gid = cred->cr_gid; +#endif /* __CYGWIN__ */ + msqptr->msg_perm.mode = (msgflg & 0777); + /* Make sure that the returned msqid is unique */ + msqptr->msg_perm.seq = (msqptr->msg_perm.seq + 1) & 0x7fff; + msqptr->msg_first = NULL; + msqptr->msg_last = NULL; + msqptr->msg_cbytes = 0; + msqptr->msg_qnum = 0; + msqptr->msg_qbytes = msginfo.msgmnb; + msqptr->msg_lspid = 0; + msqptr->msg_lrpid = 0; + msqptr->msg_stime = 0; + msqptr->msg_rtime = 0; + msqptr->msg_ctime = time (NULL); +#ifdef __CYGWIN__ + msg_info.msg_ids++; +#endif /* __CYGWIN__ */ + } else { + DPRINTF(("didn't find it and wasn't asked to create it\n")); + error = ENOENT; + goto done2; + } + +found: + /* Construct the unique msqid */ + td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); +done2: + mtx_unlock(&msq_mtx); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct msgsnd_args { + int msqid; + const void *msgp; + size_t msgsz; + int msgflg; +}; +#endif + +/* + * MPSAFE + */ +int +msgsnd(struct thread *td, struct msgsnd_args *uap) +{ + int msqid = uap->msqid; + const void *user_msgp = uap->msgp; + size_t msgsz = uap->msgsz; + int msgflg = uap->msgflg; + int segs_needed, error = 0; + register struct msqid_ds *msqptr; + register struct msg *msghdr; + short next; + + DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, + msgflg)); + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + mtx_lock(&msq_mtx); + msqid = IPCID_TO_IX(msqid); + + if (msqid < 0 || msqid >= msginfo.msgmni) { + DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, + msginfo.msgmni)); + error = EINVAL; + goto done2; + } + + msqptr = &msqids[msqid]; + if (msqptr->msg_qbytes == 0) { + DPRINTF(("no such message queue id\n")); + error = EINVAL; + goto done2; + } + if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { + DPRINTF(("wrong sequence number\n")); + error = EINVAL; + goto done2; + } + + if ((error = ipcperm(td, &msqptr->msg_perm, IPC_W))) { + DPRINTF(("requester doesn't have write access\n")); + goto done2; + } + + segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; + DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, + segs_needed)); + for (;;) { + int need_more_resources = 0; + + /* + * check msgsz + * (inside this loop in case msg_qbytes changes while we sleep) + */ + + if (msgsz > msqptr->msg_qbytes) { + DPRINTF(("msgsz > msqptr->msg_qbytes\n")); + error = EINVAL; + goto done2; + } + + if (msqptr->msg_perm.mode & MSG_LOCKED) { + DPRINTF(("msqid is locked\n")); + need_more_resources = 1; + } + if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { + DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); + need_more_resources = 1; + } + if (segs_needed > nfree_msgmaps) { + DPRINTF(("segs_needed > nfree_msgmaps\n")); + need_more_resources = 1; + } + if (free_msghdrs == NULL) { + DPRINTF(("no more msghdrs\n")); + need_more_resources = 1; + } + + if (need_more_resources) { + int we_own_it; + + if ((msgflg & IPC_NOWAIT) != 0) { + DPRINTF(("need more resources but caller " + "doesn't want to wait\n")); + error = EAGAIN; + goto done2; + } + + if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { + DPRINTF(("we don't own the msqid_ds\n")); + we_own_it = 0; + } else { + /* Force later arrivals to wait for our + request */ + DPRINTF(("we own the msqid_ds\n")); + msqptr->msg_perm.mode |= MSG_LOCKED; + we_own_it = 1; + } + DPRINTF(("goodnight\n")); + error = msleep(msqptr, &msq_mtx, (PZERO - 4) | PCATCH, + "msgwait", 0); + DPRINTF(("good morning, error=%d\n", error)); + if (we_own_it) + msqptr->msg_perm.mode &= ~MSG_LOCKED; + if (error != 0) { + DPRINTF(("msgsnd: interrupted system call\n")); +#ifdef __CYGWIN__ + if (error != EIDRM) +#endif /* __CYGWIN__ */ + error = EINTR; + goto done2; + } + + /* + * Make sure that the msq queue still exists + */ + + if (msqptr->msg_qbytes == 0) { + DPRINTF(("msqid deleted\n")); + error = EIDRM; + goto done2; + } + + } else { + DPRINTF(("got all the resources that we need\n")); + break; + } + } + + /* + * We have the resources that we need. + * Make sure! + */ + + if (msqptr->msg_perm.mode & MSG_LOCKED) + panic("msg_perm.mode & MSG_LOCKED"); + if (segs_needed > nfree_msgmaps) + panic("segs_needed > nfree_msgmaps"); + if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) + panic("msgsz + msg_cbytes > msg_qbytes"); + if (free_msghdrs == NULL) + panic("no more msghdrs"); + + /* + * Re-lock the msqid_ds in case we page-fault when copying in the + * message + */ + + if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) + panic("msqid_ds is already locked"); + msqptr->msg_perm.mode |= MSG_LOCKED; + + /* + * Allocate a message header + */ + + msghdr = free_msghdrs; + free_msghdrs = msghdr->msg_next; + msghdr->msg_spot = -1; + msghdr->msg_ts = msgsz; + + /* + * Allocate space for the message + */ + + while (segs_needed > 0) { + if (nfree_msgmaps <= 0) + panic("not enough msgmaps"); + if (free_msgmaps == -1) + panic("nil free_msgmaps"); + next = free_msgmaps; + if (next <= -1) + panic("next too low #1"); + if (next >= msginfo.msgseg) + panic("next out of range #1"); + DPRINTF(("allocating segment %d to message\n", next)); + free_msgmaps = msgmaps[next].next; + nfree_msgmaps--; + msgmaps[next].next = msghdr->msg_spot; + msghdr->msg_spot = next; + segs_needed--; + } + + /* + * Copy in the message type + */ + + mtx_unlock(&msq_mtx); + if ((error = copyin(user_msgp, &msghdr->msg_type, + sizeof(msghdr->msg_type))) != 0) { + mtx_lock(&msq_mtx); + DPRINTF(("error %d copying the message type\n", error)); + msg_freehdr(msghdr); + msqptr->msg_perm.mode &= ~MSG_LOCKED; + wakeup(msqptr); + goto done2; + } + mtx_lock(&msq_mtx); + user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type); + + /* + * Validate the message type + */ + + if (msghdr->msg_type < 1) { + msg_freehdr(msghdr); + msqptr->msg_perm.mode &= ~MSG_LOCKED; + wakeup(msqptr); + DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type)); + error = EINVAL; + goto done2; + } + + /* + * Copy in the message body + */ + + next = msghdr->msg_spot; + while (msgsz > 0) { + size_t tlen; + if (msgsz > (unsigned long) msginfo.msgssz) + tlen = msginfo.msgssz; + else + tlen = msgsz; + if (next <= -1) + panic("next too low #2"); + if (next >= msginfo.msgseg) + panic("next out of range #2"); + mtx_unlock(&msq_mtx); + if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], + tlen)) != 0) { + mtx_lock(&msq_mtx); + DPRINTF(("error %d copying in message segment\n", + error)); + msg_freehdr(msghdr); + msqptr->msg_perm.mode &= ~MSG_LOCKED; + wakeup(msqptr); + goto done2; + } + mtx_lock(&msq_mtx); + msgsz -= tlen; + user_msgp = (const char *)user_msgp + tlen; + next = msgmaps[next].next; + } + if (next != -1) + panic("didn't use all the msg segments"); + + /* + * We've got the message. Unlock the msqid_ds. + */ + + msqptr->msg_perm.mode &= ~MSG_LOCKED; + + /* + * Make sure that the msqid_ds is still allocated. + */ + + if (msqptr->msg_qbytes == 0) { + msg_freehdr(msghdr); + wakeup(msqptr); + error = EIDRM; + goto done2; + } + + /* + * Put the message into the queue + */ + + if (msqptr->msg_first == NULL) { + msqptr->msg_first = msghdr; + msqptr->msg_last = msghdr; + } else { + msqptr->msg_last->msg_next = msghdr; + msqptr->msg_last = msghdr; + } + msqptr->msg_last->msg_next = NULL; + + msqptr->msg_cbytes += msghdr->msg_ts; + msqptr->msg_qnum++; + msqptr->msg_lspid = td->td_proc->p_pid; + msqptr->msg_stime = time (NULL); + +#ifdef __CYGWIN__ + msg_info.msg_num++; + msg_info.msg_tot += uap->msgsz; +#endif /* __CYGWIN__ */ + + wakeup(msqptr); + td->td_retval[0] = 0; +done2: + mtx_unlock(&msq_mtx); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct msgrcv_args { + int msqid; + void *msgp; + size_t msgsz; + long msgtyp; + int msgflg; +}; +#endif + +/* + * MPSAFE + */ +int +msgrcv(struct thread *td, struct msgrcv_args *uap) +{ + int msqid = uap->msqid; + void *user_msgp = uap->msgp; + size_t msgsz = uap->msgsz; + long msgtyp = uap->msgtyp; + int msgflg = uap->msgflg; + size_t len; + register struct msqid_ds *msqptr; + register struct msg *msghdr; + int error = 0; + short next; + + DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, + msgsz, msgtyp, msgflg)); + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + msqid = IPCID_TO_IX(msqid); + + if (msqid < 0 || msqid >= msginfo.msgmni) { + DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, + msginfo.msgmni)); + return (EINVAL); + } + + msqptr = &msqids[msqid]; + mtx_lock(&msq_mtx); + if (msqptr->msg_qbytes == 0) { + DPRINTF(("no such message queue id\n")); + error = EINVAL; + goto done2; + } + if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { + DPRINTF(("wrong sequence number\n")); + error = EINVAL; + goto done2; + } + + if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { + DPRINTF(("requester doesn't have read access\n")); + goto done2; + } + + msghdr = NULL; + while (msghdr == NULL) { + if (msgtyp == 0) { + msghdr = msqptr->msg_first; + if (msghdr != NULL) { + if (msgsz < msghdr->msg_ts && + (msgflg & MSG_NOERROR) == 0) { + DPRINTF(("first message on the queue " + "is too big (want %d, got %d)\n", + msgsz, msghdr->msg_ts)); + error = E2BIG; + goto done2; + } + if (msqptr->msg_first == msqptr->msg_last) { + msqptr->msg_first = NULL; + msqptr->msg_last = NULL; + } else { + msqptr->msg_first = msghdr->msg_next; + if (msqptr->msg_first == NULL) + panic("msg_first/last screwed up #1"); + } + } + } else { + struct msg *previous; + struct msg **prev; + + previous = NULL; + prev = &(msqptr->msg_first); + while ((msghdr = *prev) != NULL) { + /* + * Is this message's type an exact match or is + * this message's type less than or equal to + * the absolute value of a negative msgtyp? + * Note that the second half of this test can + * NEVER be true if msgtyp is positive since + * msg_type is always positive! + */ + + if (msgtyp == msghdr->msg_type || + msghdr->msg_type <= -msgtyp) { + DPRINTF(("found message type %d, " + "requested %d\n", + msghdr->msg_type, msgtyp)); + if (msgsz < msghdr->msg_ts && + (msgflg & MSG_NOERROR) == 0) { + DPRINTF(("requested message " + "on the queue is too big " + "(want %d, got %d)\n", + msgsz, msghdr->msg_ts)); + error = E2BIG; + goto done2; + } + *prev = msghdr->msg_next; + if (msghdr == msqptr->msg_last) { + if (previous == NULL) { + if (prev != + &msqptr->msg_first) + panic("msg_first/last screwed up #2"); + msqptr->msg_first = + NULL; + msqptr->msg_last = + NULL; + } else { + if (prev == + &msqptr->msg_first) + panic("msg_first/last screwed up #3"); + msqptr->msg_last = + previous; + } + } + break; + } + previous = msghdr; + prev = &(msghdr->msg_next); + } + } + + /* + * We've either extracted the msghdr for the appropriate + * message or there isn't one. + * If there is one then bail out of this loop. + */ + + if (msghdr != NULL) + break; + + /* + * Hmph! No message found. Does the user want to wait? + */ + + if ((msgflg & IPC_NOWAIT) != 0) { + DPRINTF(("no appropriate message found (msgtyp=%d)\n", + msgtyp)); + /* The SVID says to return ENOMSG. */ + error = ENOMSG; + goto done2; + } + + /* + * Wait for something to happen + */ + + DPRINTF(("msgrcv: goodnight\n")); + error = msleep(msqptr, &msq_mtx, (PZERO - 4) | PCATCH, + "msgwait", 0); + DPRINTF(("msgrcv: good morning (error=%d)\n", error)); + + if (error != 0) { + DPRINTF(("msgsnd: interrupted system call\n")); +#ifdef __CYGWIN__ + if (error != EIDRM) +#endif /* __CYGWIN__ */ + error = EINTR; + goto done2; + } + + /* + * Make sure that the msq queue still exists + */ + + if (msqptr->msg_qbytes == 0 || + msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { + DPRINTF(("msqid deleted\n")); + error = EIDRM; + goto done2; + } + } + + /* + * Return the message to the user. + * + * First, do the bookkeeping (before we risk being interrupted). + */ + + msqptr->msg_cbytes -= msghdr->msg_ts; + msqptr->msg_qnum--; + msqptr->msg_lrpid = td->td_proc->p_pid; + msqptr->msg_rtime = time (NULL); + + /* + * Make msgsz the actual amount that we'll be returning. + * Note that this effectively truncates the message if it is too long + * (since msgsz is never increased). + */ + + DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz, + msghdr->msg_ts)); + if (msgsz > msghdr->msg_ts) + msgsz = msghdr->msg_ts; + + /* + * Return the type to the user. + */ + + mtx_unlock(&msq_mtx); + error = copyout(&(msghdr->msg_type), user_msgp, + sizeof(msghdr->msg_type)); + mtx_lock(&msq_mtx); + if (error != 0) { + DPRINTF(("error (%d) copying out message type\n", error)); + msg_freehdr(msghdr); + wakeup(msqptr); + goto done2; + } + user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); + + /* + * Return the segments to the user + */ + + next = msghdr->msg_spot; + for (len = 0; len < msgsz; len += msginfo.msgssz) { + size_t tlen; + + if (msgsz - len > (unsigned long) msginfo.msgssz) + tlen = msginfo.msgssz; + else + tlen = msgsz - len; + if (next <= -1) + panic("next too low #3"); + if (next >= msginfo.msgseg) + panic("next out of range #3"); + mtx_unlock(&msq_mtx); + error = copyout(&msgpool[next * msginfo.msgssz], + user_msgp, tlen); + mtx_lock(&msq_mtx); + if (error != 0) { + DPRINTF(("error (%d) copying out message segment\n", + error)); + msg_freehdr(msghdr); + wakeup(msqptr); + goto done2; + } + user_msgp = (char *)user_msgp + tlen; + next = msgmaps[next].next; + } + + /* + * Done, return the actual number of bytes copied out. + */ + +#ifdef __CYGWIN__ + msg_info.msg_num--; + msg_info.msg_tot -= msgsz; +#endif /* __CYGWIN__ */ + + msg_freehdr(msghdr); + wakeup(msqptr); + td->td_retval[0] = msgsz; +done2: + mtx_unlock(&msq_mtx); + return (error); +} + +#ifndef __CYGWIN__ +static int +sysctl_msqids(SYSCTL_HANDLER_ARGS) +{ + + return (SYSCTL_OUT(req, msqids, + sizeof(struct msqid_ds) * msginfo.msgmni)); +} + +SYSCTL_DECL(_kern_ipc); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0, ""); +SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD, + NULL, 0, sysctl_msqids, "", "Message queue IDs"); +#endif /* __CYGWIN__ */ +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/sysv_sem.cc b/winsup/cygserver/sysv_sem.cc new file mode 100644 index 000000000..ac5efcdaa --- /dev/null +++ b/winsup/cygserver/sysv_sem.cc @@ -0,0 +1,1323 @@ +/* + * Implementation of SVID semaphores + * + * Author: Daniel Boulet + * + * This software is provided ``AS IS'' without any warranties of any kind. + */ + +/* + * This file is heavily changed to become part of Cygwin's cygserver. + */ + +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#include +#include +#ifndef __FBSDID +#define __FBSDID(s) const char version[] = (s) +#endif +__FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/sysv_sem.c,v 1.66 2003/11/10 07:22:41 tjr Exp $"); + +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cygserver.h" +#include "process.h" +#include "cygserver_ipc.h" + +#ifdef __CYGWIN__ +#define __semctl semctl +#define __semctl_args semctl_args +#define SEM_DEBUG +#endif /* __CYGWIN__ */ + +#ifdef SEM_DEBUG +#define DPRINTF(a) debug_printf a +#else +#define DPRINTF(a) +#endif + +static int semvalid(int semid, struct semid_ds *semaptr); + +static struct sem_undo *semu_alloc(struct thread *td); +static int semundo_adjust(struct thread *td, struct sem_undo **supptr, + int semid, int semnum, int adjval); +static void semundo_clear(int semid, int semnum); + +#ifndef _SYS_SYSPROTO_H_ +struct __semctl_args; +int __semctl(struct thread *td, struct __semctl_args *uap); +struct semget_args; +int semget(struct thread *td, struct semget_args *uap); +struct semop_args; +int semop(struct thread *td, struct semop_args *uap); +#endif + +#ifndef __CYGWIN__ +/* XXX casting to (sy_call_t *) is bogus, as usual. */ +static sy_call_t *semcalls[] = { + (sy_call_t *)__semctl, (sy_call_t *)semget, + (sy_call_t *)semop +}; +#endif + +static struct mtx sem_mtx; /* semaphore global lock */ +static int semtots = 0; +static int semtot = 0; +static struct semid_ds *sema; /* semaphore id pool */ +static struct mtx *sema_mtx; /* semaphore id pool mutexes*/ +static struct sem *sem; /* semaphore pool */ +SLIST_HEAD(, sem_undo) semu_list; /* list of active undo structures */ +static int *semu; /* undo structure pool */ +#ifndef __CYGWIN__ +static eventhandler_tag semexit_tag; +#endif /* __CYGWIN__ */ + +#define SEMUNDO_MTX sem_mtx +#define SEMUNDO_LOCK() mtx_lock(&SEMUNDO_MTX); +#define SEMUNDO_HOOKLOCK() _mtx_lock(&SEMUNDO_MTX, p->winpid, __FILE__, __LINE__); +#define SEMUNDO_UNLOCK() mtx_unlock(&SEMUNDO_MTX); +#define SEMUNDO_LOCKASSERT(how) mtx_assert(&SEMUNDO_MTX, (how)); + +struct sem { + u_short semval; /* semaphore value */ + pid_t sempid; /* pid of last operation */ + u_short semncnt; /* # awaiting semval > cval */ + u_short semzcnt; /* # awaiting semval = 0 */ +}; + +/* + * Undo structure (one per process) + */ +struct undo { + short un_adjval; /* adjust on exit values */ + short un_num; /* semaphore # */ + int un_id; /* semid */ +} un_ent[1]; /* undo entries */ + +struct sem_undo { + SLIST_ENTRY(sem_undo) un_next; /* ptr to next active undo structure */ + struct proc *un_proc; /* owner of this structure */ + short un_cnt; /* # of active entries */ + struct undo un_ent[1]; /* undo entries */ +}; + +/* + * Configuration parameters + */ +#ifndef SEMMNI +#define SEMMNI 10 /* # of semaphore identifiers */ +#endif +#ifndef SEMMNS +#define SEMMNS 60 /* # of semaphores in system */ +#endif +#ifndef SEMUME +#define SEMUME 10 /* max # of undo entries per process */ +#endif +#ifndef SEMMNU +#define SEMMNU 30 /* # of undo structures in system */ +#endif + +/* shouldn't need tuning */ +#ifndef SEMMAP +#define SEMMAP 30 /* # of entries in semaphore map */ +#endif +#ifndef SEMMSL +#define SEMMSL SEMMNS /* max # of semaphores per id */ +#endif +#ifndef SEMOPM +#define SEMOPM 100 /* max # of operations per semop call */ +#endif + +#ifndef SEMVMX +#define SEMVMX 32767 /* semaphore maximum value */ +#endif +#ifndef SEMAEM +#define SEMAEM 16384 /* adjust on exit max value */ +#endif + +/* + * Due to the way semaphore memory is allocated, we have to ensure that + * SEMUSZ is properly aligned. + */ + +#define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1)) + +/* actual size of an undo structure */ +#define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME])) + +/* + * Macro to find a particular sem_undo vector + */ +#define SEMU(ix) \ + ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz)) + +/* + * semaphore info struct + */ +struct seminfo seminfo = { + SEMMNI, /* # of semaphore identifiers */ + SEMMNS, /* # of semaphores in system */ + SEMMSL, /* max # of semaphores per id */ + SEMOPM, /* max # of operations per semop call */ + SEMMNU, /* # of undo structures in system */ + SEMUME, /* max # of undo entries per process */ + SEMVMX, /* semaphore maximum value */ + SEMAEM, /* adjust on exit max value */ + SEMMAP, /* # of entries in semaphore map */ + SEMUSZ /* size in bytes of undo structure */ +}; + +#ifndef __CYGWIN__ +SYSCTL_DECL(_kern_ipc); +SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RDTUN, &seminfo.semmni, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RDTUN, &seminfo.semmns, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RDTUN, &seminfo.semmnu, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RDTUN, &seminfo.semopm, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RDTUN, &seminfo.semume, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RDTUN, &seminfo.semusz, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, ""); +SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD, + NULL, 0, sysctl_sema, "", ""); +#endif /* __CYGWIN__ */ + +void +seminit(void) +{ + int i; + + TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo.semmap); + TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo.semmni); + TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo.semmns); + TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo.semmnu); + TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo.semmsl); + TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo.semopm); + TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo.semume); + TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo.semusz); + TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo.semvmx); + TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo.semaem); + +#ifdef __CYGWIN__ + /* It's too dangerous a setting to leave it alone. + Keep that clean here. */ + seminfo.semusz = SEM_ALIGN(offsetof(struct sem_undo, + un_ent[seminfo.semume])); +#endif /* __CYGWIN__ */ + + sem = (struct sem *) sys_malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK); + sema = (struct semid_ds *) sys_malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM, + M_WAITOK); + sema_mtx = (struct mtx *) sys_malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM, + M_WAITOK | M_ZERO); + semu = (int *) sys_malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); + + for (i = 0; i < seminfo.semmni; i++) { + sema[i].sem_base = 0; + sema[i].sem_perm.mode = 0; + } + for (i = 0; i < seminfo.semmni; i++) + mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF); + for (i = 0; i < seminfo.semmnu; i++) { + struct sem_undo *suptr = SEMU(i); + suptr->un_proc = NULL; + } + SLIST_INIT(&semu_list); + mtx_init(&sem_mtx, "sem", NULL, MTX_DEF); +#ifndef __CYGWIN__ + semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL, + EVENTHANDLER_PRI_ANY); +#endif /* __CYGWIN__ */ +} + +int +semunload(void) +{ +#ifndef __CYGWIN__ /* Would result in being unable to shutdown the + server gracefully. */ + if (semtot != 0) + return (EBUSY); + + EVENTHANDLER_DEREGISTER(process_exit, semexit_tag); +#endif /* __CYGWIN__ */ + sys_free(sem, M_SEM); + sys_free(sema, M_SEM); + sys_free(semu, M_SEM); + for (int i = 0; i < seminfo.semmni; i++) + mtx_destroy(&sema_mtx[i]); + mtx_destroy(&sem_mtx); + return (0); +} + +#ifndef __CYGWIN__ +static int +sysvsem_modload(struct module *module, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + seminit(); + break; + case MOD_UNLOAD: + error = semunload(); + break; + case MOD_SHUTDOWN: + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static moduledata_t sysvsem_mod = { + "sysvsem", + &sysvsem_modload, + NULL +}; + +SYSCALL_MODULE_HELPER(semsys); +SYSCALL_MODULE_HELPER(__semctl); +SYSCALL_MODULE_HELPER(semget); +SYSCALL_MODULE_HELPER(semop); + +DECLARE_MODULE(sysvsem, sysvsem_mod, + SI_SUB_SYSV_SEM, SI_ORDER_FIRST); +MODULE_VERSION(sysvsem, 1); + +/* + * Entry point for all SEM calls + * + * MPSAFE + */ +int +semsys(td, uap) + struct thread *td; + /* XXX actually varargs. */ + struct semsys_args /* { + int which; + int a2; + int a3; + int a4; + int a5; + } */ *uap; +{ + int error; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + if (uap->which < 0 || + uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) + return (EINVAL); + error = (*semcalls[uap->which])(td, &uap->a2); + return (error); +} +#endif /* __CYGWIN__ */ + +/* + * Allocate a new sem_undo structure for a process + * (returns ptr to structure or NULL if no more room) + */ + +static struct sem_undo * +semu_alloc(struct thread *td) +{ + int i; + struct sem_undo *suptr; + struct sem_undo **supptr; + int attempt; + + SEMUNDO_LOCKASSERT(MA_OWNED); + /* + * Try twice to allocate something. + * (we'll purge an empty structure after the first pass so + * two passes are always enough) + */ + + for (attempt = 0; attempt < 2; attempt++) { + /* + * Look for a free structure. + * Fill it in and return it if we find one. + */ + + for (i = 0; i < seminfo.semmnu; i++) { + suptr = SEMU(i); + if (suptr->un_proc == NULL) { + SLIST_INSERT_HEAD(&semu_list, suptr, un_next); + suptr->un_cnt = 0; + suptr->un_proc = td->td_proc; + return(suptr); + } + } + + /* + * We didn't find a free one, if this is the first attempt + * then try to free a structure. + */ + + if (attempt == 0) { + /* All the structures are in use - try to free one */ + int did_something = 0; + + SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, + un_next) { + if (suptr->un_cnt == 0) { + suptr->un_proc = NULL; + did_something = 1; + *supptr = SLIST_NEXT(suptr, un_next); + break; + } + } + + /* If we didn't free anything then just give-up */ + if (!did_something) + return(NULL); + } else { + /* + * The second pass failed even though we freed + * something after the first pass! + * This is IMPOSSIBLE! + */ + panic("semu_alloc - second attempt failed"); + } + } + return (NULL); +} + +/* + * Adjust a particular entry for a particular proc + */ + +static int +semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid, + int semnum, int adjval) +{ + struct proc *p = td->td_proc; + struct sem_undo *suptr; + struct undo *sunptr; + int i; + + SEMUNDO_LOCKASSERT(MA_OWNED); + /* Look for and remember the sem_undo if the caller doesn't provide + it */ + + suptr = *supptr; + if (suptr == NULL) { + SLIST_FOREACH(suptr, &semu_list, un_next) { + if (suptr->un_proc == p) { + *supptr = suptr; + break; + } + } + if (suptr == NULL) { + if (adjval == 0) + return(0); + suptr = semu_alloc(td); + if (suptr == NULL) + return(ENOSPC); + *supptr = suptr; + } + } + + /* + * Look for the requested entry and adjust it (delete if adjval becomes + * 0). + */ + sunptr = &suptr->un_ent[0]; + for (i = 0; i < suptr->un_cnt; i++, sunptr++) { + if (sunptr->un_id != semid || sunptr->un_num != semnum) + continue; + if (adjval != 0) { + adjval += sunptr->un_adjval; + if (adjval > seminfo.semaem || adjval < -seminfo.semaem) + return (ERANGE); + } + sunptr->un_adjval = adjval; + if (sunptr->un_adjval == 0) { + suptr->un_cnt--; + if (i < suptr->un_cnt) + suptr->un_ent[i] = + suptr->un_ent[suptr->un_cnt]; + } + return(0); + } + + /* Didn't find the right entry - create it */ + if (adjval == 0) + return(0); + if (adjval > seminfo.semaem || adjval < -seminfo.semaem) + return (ERANGE); + if (suptr->un_cnt != seminfo.semume) { + sunptr = &suptr->un_ent[suptr->un_cnt]; + suptr->un_cnt++; + sunptr->un_adjval = adjval; + sunptr->un_id = semid; sunptr->un_num = semnum; + } else + return(EINVAL); + return(0); +} + +static void +semundo_clear(int semid, int semnum) +{ + struct sem_undo *suptr; + + SEMUNDO_LOCKASSERT(MA_OWNED); + SLIST_FOREACH(suptr, &semu_list, un_next) { + struct undo *sunptr = &suptr->un_ent[0]; + int i = 0; + + while (i < suptr->un_cnt) { + if (sunptr->un_id == semid) { + if (semnum == -1 || sunptr->un_num == semnum) { + suptr->un_cnt--; + if (i < suptr->un_cnt) { + suptr->un_ent[i] = + suptr->un_ent[suptr->un_cnt]; + continue; + } + } + if (semnum != -1) + break; + } + i++, sunptr++; + } + } +} + +static int +semvalid(int semid, struct semid_ds *semaptr) +{ + + return ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || + semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0); +} + +/* + * Note that the user-mode half of this passes a union, not a pointer + */ +#ifndef _SYS_SYSPROTO_H_ +struct __semctl_args { + int semid; + int semnum; + int cmd; + union semun *arg; +}; +#endif + +/* + * MPSAFE + */ +int +__semctl(struct thread *td, struct __semctl_args *uap) +{ + int semid = uap->semid; + int semnum = uap->semnum; + int cmd = uap->cmd; + u_short *array; + union semun *arg = uap->arg; + union semun real_arg; +#ifndef __CYGWIN__ + struct ucred *cred = td->td_ucred; +#endif + int i, rval, error; + struct semid_ds sbuf; + struct semid_ds *semaptr; + struct mtx *sema_mtxp; + u_short usval, count; + + DPRINTF(("call to semctl(%d, %d, %d, 0x%x)\n", + semid, semnum, cmd, arg)); + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + array = NULL; + + switch(cmd) { +#ifdef __CYGWIN__ + case IPC_INFO: + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); + if (!semid) { + error = copyout(&seminfo, real_arg.buf, + sizeof(struct seminfo)); + td->td_retval[0] = error ? -1 : 0; + return (error); + } + if (semid > seminfo.semmni) + semid = seminfo.semmni; + error = copyout(sema, real_arg.buf, + semid * sizeof(struct semid_ds)); + td->td_retval[0] = error ? -1 : 0; + return (error); + case SEM_INFO: + if (!(error = copyin(arg, &real_arg, sizeof(real_arg)))) { + struct sem_info sem_info; + sem_info.sem_ids = semtots; + sem_info.sem_num = semtot; + error = copyout(&sem_info, real_arg.buf, + sizeof(struct sem_info)); + } + td->td_retval[0] = error ? -1 : 0; + return (error); + +#endif /* __CYGWIN__ */ + case SEM_STAT: + if (semid < 0 || semid >= seminfo.semmni) + return (EINVAL); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); + semaptr = &sema[semid]; + sema_mtxp = &sema_mtx[semid]; + mtx_lock(sema_mtxp); + if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) { + error = EINVAL; + goto done2; + } + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + mtx_unlock(sema_mtxp); + error = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds)); + rval = IXSEQ_TO_IPCID(semid,semaptr->sem_perm); + if (error == 0) + td->td_retval[0] = rval; + return (error); + } + + semid = IPCID_TO_IX(semid); + if (semid < 0 || semid >= seminfo.semmni) + return (EINVAL); + + semaptr = &sema[semid]; + sema_mtxp = &sema_mtx[semid]; + + error = 0; + rval = 0; + + switch (cmd) { + case IPC_RMID: + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M))) + goto done2; +#ifdef __CYGWIN__ + semaptr->sem_perm.cuid = td->ipcblk->uid; + semaptr->sem_perm.uid = td->ipcblk->uid; +#else + semaptr->sem_perm.cuid = cred->cr_uid; + semaptr->sem_perm.uid = cred->cr_uid; +#endif + semtot -= semaptr->sem_nsems; + semtots--; + for (i = semaptr->sem_base - sem; i < semtot; i++) + sem[i] = sem[i + semaptr->sem_nsems]; + for (i = 0; i < seminfo.semmni; i++) { + if ((sema[i].sem_perm.mode & SEM_ALLOC) && + sema[i].sem_base > semaptr->sem_base) + sema[i].sem_base -= semaptr->sem_nsems; + } + semaptr->sem_perm.mode = 0; + SEMUNDO_LOCK(); + semundo_clear(semid, -1); + SEMUNDO_UNLOCK(); + wakeup(semaptr); + break; + + case IPC_SET: + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + goto done2; + if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0) + goto done2; + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M))) + goto done2; + semaptr->sem_perm.uid = sbuf.sem_perm.uid; + semaptr->sem_perm.gid = sbuf.sem_perm.gid; + semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | + (sbuf.sem_perm.mode & 0777); + semaptr->sem_ctime = time (NULL); + break; + + case IPC_STAT: + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + goto done2; + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + sbuf = *semaptr; + mtx_unlock(sema_mtxp); + error = copyout(semaptr, real_arg.buf, + sizeof(struct semid_ds)); + break; + + case GETNCNT: + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + if (semnum < 0 || semnum >= semaptr->sem_nsems) { + error = EINVAL; + goto done2; + } + rval = semaptr->sem_base[semnum].semncnt; + break; + + case GETPID: + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + if (semnum < 0 || semnum >= semaptr->sem_nsems) { + error = EINVAL; + goto done2; + } + rval = semaptr->sem_base[semnum].sempid; + break; + + case GETVAL: + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + if (semnum < 0 || semnum >= semaptr->sem_nsems) { + error = EINVAL; + goto done2; + } + rval = semaptr->sem_base[semnum].semval; + break; + + case GETALL: + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + goto done2; + array = (u_short *) sys_malloc(sizeof(*array) * semaptr->sem_nsems, M_TEMP, + M_WAITOK); + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + for (i = 0; i < semaptr->sem_nsems; i++) + array[i] = semaptr->sem_base[i].semval; + mtx_unlock(sema_mtxp); + error = copyout(array, real_arg.array, + i * sizeof(real_arg.array[0])); + break; + + case GETZCNT: + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) + goto done2; + if (semnum < 0 || semnum >= semaptr->sem_nsems) { + error = EINVAL; + goto done2; + } + rval = semaptr->sem_base[semnum].semzcnt; + break; + + case SETVAL: + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + goto done2; + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W))) + goto done2; + if (semnum < 0 || semnum >= semaptr->sem_nsems) { + error = EINVAL; + goto done2; + } + if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) { + error = ERANGE; + goto done2; + } + semaptr->sem_base[semnum].semval = real_arg.val; + SEMUNDO_LOCK(); + semundo_clear(semid, semnum); + SEMUNDO_UNLOCK(); + wakeup(semaptr); + break; + + case SETALL: + mtx_lock(sema_mtxp); +raced: + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + count = semaptr->sem_nsems; + mtx_unlock(sema_mtxp); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + goto done2; + array = (u_short *) sys_malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); + copyin(real_arg.array, array, count * sizeof(*array)); + if (error) + break; + mtx_lock(sema_mtxp); + if ((error = semvalid(uap->semid, semaptr)) != 0) + goto done2; + /* we could have raced? */ + if (count != semaptr->sem_nsems) { + sys_free(array, M_TEMP); + array = NULL; + goto raced; + } + if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W))) + goto done2; + for (i = 0; i < semaptr->sem_nsems; i++) { + usval = array[i]; + if (usval > seminfo.semvmx) { + error = ERANGE; + break; + } + semaptr->sem_base[i].semval = usval; + } + SEMUNDO_LOCK(); + semundo_clear(semid, -1); + SEMUNDO_UNLOCK(); + wakeup(semaptr); + break; + + default: + error = EINVAL; + break; + } + + if (error == 0) + td->td_retval[0] = rval; +done2: + if (mtx_owned(sema_mtxp)) + mtx_unlock(sema_mtxp); + if (array != NULL) + sys_free(array, M_TEMP); + return(error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct semget_args { + key_t key; + int nsems; + int semflg; +}; +#endif + +/* + * MPSAFE + */ +int +semget(struct thread *td, struct semget_args *uap) +{ + int semid, error = 0; + key_t key = uap->key; + int nsems = uap->nsems; + int semflg = uap->semflg; +#ifndef __CYGWIN__ + struct ucred *cred = td->td_ucred; +#endif + + DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg)); + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + mtx_lock(&Giant); + if (key != IPC_PRIVATE) { + for (semid = 0; semid < seminfo.semmni; semid++) { + if ((sema[semid].sem_perm.mode & SEM_ALLOC) && + sema[semid].sem_perm.key == key) + break; + } + if (semid < seminfo.semmni) { + DPRINTF(("found public key\n")); + if ((error = ipcperm(td, &sema[semid].sem_perm, + semflg & 0700))) { + goto done2; + } + if (nsems > 0 && sema[semid].sem_nsems < nsems) { + DPRINTF(("too small\n")); + error = EINVAL; + goto done2; + } + if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { + DPRINTF(("not exclusive\n")); + error = EEXIST; + goto done2; + } + goto found; + } + } + + DPRINTF(("need to allocate the semid_ds\n")); + if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { + if (nsems <= 0 || nsems > seminfo.semmsl) { + DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, + seminfo.semmsl)); + error = EINVAL; + goto done2; + } + if (nsems > seminfo.semmns - semtot) { + DPRINTF(( + "not enough semaphores left (need %d, got %d)\n", + nsems, seminfo.semmns - semtot)); + error = ENOSPC; + goto done2; + } + for (semid = 0; semid < seminfo.semmni; semid++) { + if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) + break; + } + if (semid == seminfo.semmni) { + DPRINTF(("no more semid_ds's available\n")); + error = ENOSPC; + goto done2; + } + DPRINTF(("semid %d is available\n", semid)); + sema[semid].sem_perm.key = key; +#ifdef __CYGWIN__ + sema[semid].sem_perm.cuid = td->ipcblk->uid; + sema[semid].sem_perm.uid = td->ipcblk->uid; + sema[semid].sem_perm.cgid = td->ipcblk->gid; + sema[semid].sem_perm.gid = td->ipcblk->gid; +#else + sema[semid].sem_perm.cuid = cred->cr_uid; + sema[semid].sem_perm.uid = cred->cr_uid; + sema[semid].sem_perm.cgid = cred->cr_gid; + sema[semid].sem_perm.gid = cred->cr_gid; +#endif + sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; + sema[semid].sem_perm.seq = + (sema[semid].sem_perm.seq + 1) & 0x7fff; + sema[semid].sem_nsems = nsems; + sema[semid].sem_otime = 0; + sema[semid].sem_ctime = time (NULL); + sema[semid].sem_base = &sem[semtot]; + semtot += nsems; + semtots++; + bzero(sema[semid].sem_base, + sizeof(sema[semid].sem_base[0])*nsems); + DPRINTF(("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, + &sem[semtot])); + } else { + DPRINTF(("didn't find it and wasn't asked to create it\n")); + error = ENOENT; + goto done2; + } + +found: + td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); +done2: +#ifdef __CYGWIN__ + if (!error) + ipcexit_creat_hookthread (td); +#endif + mtx_unlock(&Giant); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct semop_args { + int semid; + struct sembuf *sops; + size_t nsops; +}; +#endif + +/* + * MPSAFE + */ +int +semop(struct thread *td, struct semop_args *uap) +{ + int semid = uap->semid; + size_t nsops = uap->nsops; + struct sembuf *sops; + struct semid_ds *semaptr; + struct sembuf *sopptr = 0; + struct sem *semptr = 0; + struct sem_undo *suptr; + struct mtx *sema_mtxp; + size_t i, j, k; + int error; + int do_wakeup, do_undos; + + DPRINTF(("call to semop(%d, 0x%x, %u)\n", semid, uap->sops, nsops)); + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ + + if (semid < 0 || semid >= seminfo.semmni) + return (EINVAL); + + /* Allocate memory for sem_ops */ + if (nsops > (unsigned long) seminfo.semopm) { + DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, + nsops)); + return (E2BIG); + } + sops = (struct sembuf *) sys_malloc(nsops * sizeof(sops[0]), M_SEM, M_WAITOK); + if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) { + DPRINTF(("error = %d from copyin(%08x, %08x, %d)\n", error, + uap->sops, sops, nsops * sizeof(sops[0]))); + sys_free(sops, M_SEM); + return (error); + } + + semaptr = &sema[semid]; + sema_mtxp = &sema_mtx[semid]; + mtx_lock(sema_mtxp); + if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) { + error = EINVAL; + goto done2; + } + if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { + error = EINVAL; + goto done2; + } + /* + * Initial pass thru sops to see what permissions are needed. + * Also perform any checks that don't need repeating on each + * attempt to satisfy the request vector. + */ + j = 0; /* permission needed */ + do_undos = 0; + for (i = 0; i < nsops; i++) { + sopptr = &sops[i]; + if (sopptr->sem_num >= semaptr->sem_nsems) { + error = EFBIG; + goto done2; + } + if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0) + do_undos = 1; + j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A; + } + + if ((error = ipcperm(td, &semaptr->sem_perm, j))) { + DPRINTF(("error = %d from ipaccess\n", error)); + goto done2; + } + + /* + * Loop trying to satisfy the vector of requests. + * If we reach a point where we must wait, any requests already + * performed are rolled back and we go to sleep until some other + * process wakes us up. At this point, we start all over again. + * + * This ensures that from the perspective of other tasks, a set + * of requests is atomic (never partially satisfied). + */ + for (;;) { + do_wakeup = 0; + error = 0; /* error return if necessary */ + + for (i = 0; i < nsops; i++) { + sopptr = &sops[i]; + semptr = &semaptr->sem_base[sopptr->sem_num]; + + DPRINTF(( + "semop: semaptr=%x, sem_base=%x, " + "semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", + semaptr, semaptr->sem_base, semptr, + sopptr->sem_num, semptr->semval, sopptr->sem_op, + (sopptr->sem_flg & IPC_NOWAIT) ? + "nowait" : "wait")); + + if (sopptr->sem_op < 0) { + if (semptr->semval + sopptr->sem_op < 0) { + DPRINTF(("semop: can't do it now\n")); + break; + } else { + semptr->semval += sopptr->sem_op; + if (semptr->semval == 0 && + semptr->semzcnt > 0) + do_wakeup = 1; + } + } else if (sopptr->sem_op == 0) { + if (semptr->semval != 0) { + DPRINTF(("semop: not zero now\n")); + break; + } + } else if (semptr->semval + sopptr->sem_op > + seminfo.semvmx) { + error = ERANGE; + break; + } else { + if (semptr->semncnt > 0) + do_wakeup = 1; + semptr->semval += sopptr->sem_op; + } + } + + /* + * Did we get through the entire vector? + */ + if (i >= nsops) + goto done; + + /* + * No ... rollback anything that we've already done + */ + DPRINTF(("semop: rollback 0 through %d\n", i-1)); + for (j = 0; j < i; j++) + semaptr->sem_base[sops[j].sem_num].semval -= + sops[j].sem_op; + + /* If we detected an error, return it */ + if (error != 0) + goto done2; + + /* + * If the request that we couldn't satisfy has the + * NOWAIT flag set then return with EAGAIN. + */ + if (sopptr->sem_flg & IPC_NOWAIT) { + error = EAGAIN; + goto done2; + } + + if (sopptr->sem_op == 0) + semptr->semzcnt++; + else + semptr->semncnt++; + + DPRINTF(("semop: good night!\n")); + error = msleep(semaptr, sema_mtxp, (PZERO - 4) | PCATCH, + "semwait", 0); + DPRINTF(("semop: good morning (error=%d)!\n", error)); + + if (error != 0) { +#ifdef __CYGWIN__ + if (error != EIDRM) +#endif /* __CYGWIN__ */ + error = EINTR; + goto done2; + } + DPRINTF(("semop: good morning!\n")); + + /* + * Make sure that the semaphore still exists + */ + if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || + semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { + error = EIDRM; + goto done2; + } + + /* + * The semaphore is still alive. Readjust the count of + * waiting processes. + */ + if (sopptr->sem_op == 0) + semptr->semzcnt--; + else + semptr->semncnt--; + } + +done: + /* + * Process any SEM_UNDO requests. + */ + if (do_undos) { + SEMUNDO_LOCK(); + suptr = NULL; + for (i = 0; i < nsops; i++) { + /* + * We only need to deal with SEM_UNDO's for non-zero + * op's. + */ + int adjval; + + if ((sops[i].sem_flg & SEM_UNDO) == 0) + continue; + adjval = sops[i].sem_op; + if (adjval == 0) + continue; + error = semundo_adjust(td, &suptr, semid, + sops[i].sem_num, -adjval); + if (error == 0) + continue; + + /* + * Oh-Oh! We ran out of either sem_undo's or undo's. + * Rollback the adjustments to this point and then + * rollback the semaphore ups and down so we can return + * with an error with all structures restored. We + * rollback the undo's in the exact reverse order that + * we applied them. This guarantees that we won't run + * out of space as we roll things back out. + */ + for (j = 0; j < i; j++) { + k = i - j - 1; + if ((sops[k].sem_flg & SEM_UNDO) == 0) + continue; + adjval = sops[k].sem_op; + if (adjval == 0) + continue; + if (semundo_adjust(td, &suptr, semid, + sops[k].sem_num, adjval) != 0) + panic("semop - can't undo undos"); + } + + for (j = 0; j < nsops; j++) + semaptr->sem_base[sops[j].sem_num].semval -= + sops[j].sem_op; + + DPRINTF(("error = %d from semundo_adjust\n", error)); + SEMUNDO_UNLOCK(); + goto done2; + } /* loop through the sops */ + SEMUNDO_UNLOCK(); + } /* if (do_undos) */ + + /* We're definitely done - set the sempid's and time */ + for (i = 0; i < nsops; i++) { + sopptr = &sops[i]; + semptr = &semaptr->sem_base[sopptr->sem_num]; + semptr->sempid = td->td_proc->p_pid; + } + semaptr->sem_otime = time (NULL); + + /* + * Do a wakeup if any semaphore was up'd whilst something was + * sleeping on it. + */ + if (do_wakeup) { + DPRINTF(("semop: doing wakeup\n")); + wakeup(semaptr); + DPRINTF(("semop: back from wakeup\n")); + } + DPRINTF(("semop: done\n")); + td->td_retval[0] = 0; +done2: + mtx_unlock(sema_mtxp); + sys_free(sops, M_SEM); + return (error); +} + +/* + * Go through the undo structures for this process and apply the adjustments to + * semaphores. + */ +void +semexit_myhook(void *arg, struct proc *p) +{ + struct sem_undo *suptr; + struct sem_undo **supptr; + +#ifdef __CYGWIN__ + /* + * Search all mutexes, if some of them are still owned by the + * leaving process. If so, unlock them. + */ + if (sem_mtx.owner == p->winpid) + mtx_unlock(&sem_mtx); + for (int i = 0; i < seminfo.semmni; i++) + if (sema_mtx[i].owner == p->winpid) + mtx_unlock(&sema_mtx[i]); +#endif /* __CYGWIN__ */ + + /* + * Go through the chain of undo vectors looking for one + * associated with this process. + */ + SEMUNDO_HOOKLOCK(); + SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) { + if (suptr->un_proc == p) + break; + } + SEMUNDO_UNLOCK(); + + if (suptr == NULL) + return; + + DPRINTF(("proc @%08x has undo structure with %d entries\n", p, + suptr->un_cnt)); + + /* + * If there are any active undo elements then process them. + */ + if (suptr->un_cnt > 0) { + int ix; + + for (ix = 0; ix < suptr->un_cnt; ix++) { + int semid = suptr->un_ent[ix].un_id; + int semnum = suptr->un_ent[ix].un_num; + int adjval = suptr->un_ent[ix].un_adjval; + struct semid_ds *semaptr; + struct mtx *sema_mtxp; + + semaptr = &sema[semid]; + sema_mtxp = &sema_mtx[semid]; +#ifdef __CYGWIN__ + _mtx_lock(sema_mtxp, p->winpid, __FILE__, __LINE__); +#else + mtx_lock(sema_mtxp); +#endif + SEMUNDO_HOOKLOCK(); + if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) + panic("semexit - semid not allocated"); + if (semnum >= semaptr->sem_nsems) + panic("semexit - semnum out of range"); + + DPRINTF(( + "semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", + suptr->un_proc, suptr->un_ent[ix].un_id, + suptr->un_ent[ix].un_num, + suptr->un_ent[ix].un_adjval, + semaptr->sem_base[semnum].semval)); + + if (adjval < 0) { + if (semaptr->sem_base[semnum].semval < -adjval) + semaptr->sem_base[semnum].semval = 0; + else + semaptr->sem_base[semnum].semval += + adjval; + } else + semaptr->sem_base[semnum].semval += adjval; + + wakeup(semaptr); + DPRINTF(("semexit: back from wakeup\n")); + mtx_unlock(sema_mtxp); + SEMUNDO_UNLOCK(); + } + } + + /* + * Deallocate the undo vector. + */ + DPRINTF(("removing vector\n")); + suptr->un_proc = NULL; + *supptr = SLIST_NEXT(suptr, un_next); +} + +#ifndef __CYGWIN__ +static int +sysctl_sema(SYSCTL_HANDLER_ARGS) +{ + + return (SYSCTL_OUT(req, sema, + sizeof(struct semid_ds) * seminfo.semmni)); +} +#endif /* __CYGWIN__ */ +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/sysv_shm.cc b/winsup/cygserver/sysv_shm.cc new file mode 100644 index 000000000..70ed1a3d4 --- /dev/null +++ b/winsup/cygserver/sysv_shm.cc @@ -0,0 +1,1025 @@ +/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ +/* + * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Glass and Charles + * Hannum. + * 4. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file is heavily changed to become part of Cygwin's cygserver. + */ + +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#include +#ifndef __FBSDID +#define __FBSDID(s) const char version[] = (s) +#endif +__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/kern/sysv_shm.c,v 1.89 2003/11/07 04:47:14 rwatson Exp $"); + +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cygserver.h" +#include "process.h" +#include "cygserver_ipc.h" + +#ifdef __CYGWIN__ +#ifndef PAGE_SIZE +#define PAGE_SIZE (getpagesize ()) +#endif +#ifndef PAGE_MASK +#define PAGE_MASK (PAGE_SIZE - 1) +#endif +#define btoc(b) (((b) + PAGE_MASK) / PAGE_SIZE) +#define round_page(p) ((((unsigned long)(p)) + PAGE_MASK) & ~(PAGE_MASK)) +#define ACCESSPERMS (0777) +#define GIANT_REQUIRED mtx_assert(&Giant, MA_OWNED) +#define KERN_SUCCESS 0 +#define VM_PROT_READ PROT_READ +#define VM_PROT_WRITE PROT_WRITE +#define VM_INHERIT_SHARE 0 +#define OBJT_PHYS 0 +#define OBJT_SWAP 0 +#define VM_PROT_DEFAULT 0 +#define VM_OBJECT_LOCK(a) +#define vm_object_clear_flag(a,b) +#define vm_object_set_flag(a,b) +#define VM_OBJECT_UNLOCK(a) +#define vm_object_reference(a) +#define vm_map_remove(a,b,c) KERN_SUCCESS +#define vm_map_find(a,b,c,d,e,f,g,h,i) KERN_SUCCESS +#define vm_map_inherit(a,b,c,d) +typedef int vm_prot_t; +#endif /* __CYGWIN__ */ + +#ifndef __CYGWIN__ +static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); + +struct oshmctl_args; +static int oshmctl(struct thread *td, struct oshmctl_args *uap); +#endif /* __CYGWIN__ */ + +static int shmget_allocate_segment(struct thread *td, + struct shmget_args *uap, int mode); +static int shmget_existing(struct thread *td, struct shmget_args *uap, + int mode, int segnum); + +#ifndef __CYGWIN__ +/* XXX casting to (sy_call_t *) is bogus, as usual. */ +static sy_call_t *shmcalls[] = { + (sy_call_t *)shmat, (sy_call_t *)oshmctl, + (sy_call_t *)shmdt, (sy_call_t *)shmget, + (sy_call_t *)shmctl +}; +#endif /* __CYGWIN__ */ + +#define SHMSEG_FREE 0x0200 +#define SHMSEG_REMOVED 0x0400 +#define SHMSEG_ALLOCATED 0x0800 +#define SHMSEG_WANTED 0x1000 + +static int shm_last_free, shm_nused, shm_committed, shmalloced, shm_nattch; +static struct shmid_ds *shmsegs; + +struct shm_handle { + /* vm_offset_t kva; */ + vm_object_t shm_object; +}; + +struct shmmap_state { + vm_offset_t va; + int shmid; +}; + +static void shm_deallocate_segment(struct shmid_ds *); +static int shm_find_segment_by_key(key_t); +static struct shmid_ds *shm_find_segment_by_shmid(int); +static struct shmid_ds *shm_find_segment_by_shmidx(int); +static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); +static void shmrealloc(void); + +/* + * Tuneable values. + */ +#ifndef SHMMAXPGS +#define SHMMAXPGS 8192 /* Note: sysv shared memory is swap backed. */ +#endif +#ifndef SHMMAX +#define SHMMAX (SHMMAXPGS*PAGE_SIZE) +#endif +#ifndef SHMMIN +#define SHMMIN 1 +#endif +#ifndef SHMMNI +#define SHMMNI 192 +#endif +#ifndef SHMSEG +#define SHMSEG 128 +#endif +#ifndef SHMALL +#define SHMALL (SHMMAXPGS) +#endif + +struct shminfo shminfo = { + SHMMAX, + SHMMIN, + SHMMNI, + SHMSEG, + SHMALL +}; + +#ifndef __CYGWIN__ +static int shm_use_phys; +#else +static long shm_use_phys; +static long shm_allow_removed; +#endif /* __CYGWIN__ */ + +#ifndef __CYGWIN__ +struct shm_info shm_info; +#endif /* __CYGWIN__ */ + +#ifndef __CYGWIN__ +SYSCTL_DECL(_kern_ipc); +SYSCTL_INT(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RW, &shminfo.shmmax, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RW, &shminfo.shmmin, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RW, &shminfo.shmall, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RW, + &shm_use_phys, 0, ""); +SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RW, + &shm_allow_removed, 0, ""); +SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLFLAG_RD, + NULL, 0, sysctl_shmsegs, "", ""); +#endif /* __CYGWIN__ */ + +static int +shm_find_segment_by_key(key_t key) +{ + int i; + + for (i = 0; i < shmalloced; i++) + if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && + shmsegs[i].shm_perm.key == key) + return (i); + return (-1); +} + +static struct shmid_ds * +shm_find_segment_by_shmid(int shmid) +{ + int segnum; + struct shmid_ds *shmseg; + + segnum = IPCID_TO_IX(shmid); + if (segnum < 0 || segnum >= shmalloced) + return (NULL); + shmseg = &shmsegs[segnum]; + if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 || + (!shm_allow_removed && + (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0) || + shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) + return (NULL); + return (shmseg); +} + +static struct shmid_ds * +shm_find_segment_by_shmidx(int segnum) +{ + struct shmid_ds *shmseg; + + if (segnum < 0 || segnum >= shmalloced) + return (NULL); + shmseg = &shmsegs[segnum]; + if ((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) == 0 || + (!shm_allow_removed && + (shmseg->shm_perm.mode & SHMSEG_REMOVED) != 0)) + return (NULL); + return (shmseg); +} + +static void +shm_deallocate_segment(struct shmid_ds *shmseg) +{ + struct shm_handle *shm_handle; + size_t size; + + GIANT_REQUIRED; + + shm_handle = shmseg->shm_internal; + vm_object_deallocate(shm_handle->shm_object); + sys_free(shm_handle, M_SHM); + shmseg->shm_internal = NULL; + size = round_page(shmseg->shm_segsz); + shm_committed -= btoc(size); + shm_nused--; + shmseg->shm_perm.mode = SHMSEG_FREE; +} + +static int +shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) +{ + struct shmid_ds *shmseg; + int segnum, result; + size_t size; + + GIANT_REQUIRED; + + segnum = IPCID_TO_IX(shmmap_s->shmid); + shmseg = &shmsegs[segnum]; + size = round_page(shmseg->shm_segsz); + result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); + if (result != KERN_SUCCESS) + return (EINVAL); + shmmap_s->shmid = -1; + shmseg->shm_dtime = time (NULL); + --shm_nattch; + if ((--shmseg->shm_nattch <= 0) && + (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { + shm_deallocate_segment(shmseg); + shm_last_free = segnum; + } + return (0); +} + +#ifndef _SYS_SYSPROTO_H_ +struct shmdt_args { + const void *shmaddr; +}; +#endif + +/* + * MPSAFE + */ +int +shmdt(struct thread *td, struct shmdt_args *uap) +{ + struct proc *p = td->td_proc; + struct shmmap_state *shmmap_s; + int i; + int error = 0; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + mtx_lock(&Giant); + shmmap_s = p->p_vmspace->vm_shm; + if (shmmap_s == NULL) { + error = EINVAL; + goto done2; + } + for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { + if (shmmap_s->shmid != -1 && + shmmap_s->va == (vm_offset_t)uap->shmaddr) { + break; + } + } + if (i == shminfo.shmseg) { + error = EINVAL; + goto done2; + } + error = shm_delete_mapping(p->p_vmspace, shmmap_s); +done2: + mtx_unlock(&Giant); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct shmat_args { + int shmid; + const void *shmaddr; + int shmflg; +}; +#endif + +/* + * MPSAFE + */ +int +kern_shmat(struct thread *td, int shmid, const void *shmaddr, int shmflg) +{ + struct proc *p = td->td_proc; + int i, flags; + struct shmid_ds *shmseg; + struct shmmap_state *shmmap_s = NULL; + struct shm_handle *shm_handle; + vm_offset_t attach_va; + vm_prot_t prot; + vm_size_t size; + int rv; + int error = 0; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + mtx_lock(&Giant); + shmmap_s = p->p_vmspace->vm_shm; + if (shmmap_s == NULL) { + size = shminfo.shmseg * sizeof(struct shmmap_state); + shmmap_s = (struct shmmap_state *) sys_malloc(size, M_SHM, M_WAITOK); + for (i = 0; i < shminfo.shmseg; i++) + shmmap_s[i].shmid = -1; + p->p_vmspace->vm_shm = shmmap_s; + } + shmseg = shm_find_segment_by_shmid(shmid); + if (shmseg == NULL) { + error = EINVAL; + goto done2; + } + error = ipcperm(td, &shmseg->shm_perm, + (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); + if (error) + goto done2; + for (i = 0; i < shminfo.shmseg; i++) { + if (shmmap_s->shmid == -1) + break; + shmmap_s++; + } + if (i >= shminfo.shmseg) { + error = EMFILE; + goto done2; + } + size = round_page(shmseg->shm_segsz); +#ifdef VM_PROT_READ_IS_EXEC + prot = VM_PROT_READ | VM_PROT_EXECUTE; +#else + prot = VM_PROT_READ; +#endif + if ((shmflg & SHM_RDONLY) == 0) + prot |= VM_PROT_WRITE; + flags = MAP_ANON | MAP_SHARED; + if (shmaddr) { + flags |= MAP_FIXED; + if (shmflg & SHM_RND) { + attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1); + } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) { + attach_va = (vm_offset_t)shmaddr; + } else { + error = EINVAL; + goto done2; + } + } else { + /* + * This is just a hint to vm_map_find() about where to + * put it. + */ +#ifdef __CYGWIN__ + attach_va = 0; +#else + attach_va = round_page((vm_offset_t)p->p_vmspace->vm_taddr + + maxtsiz + maxdsiz); +#endif + } + + shm_handle = shmseg->shm_internal; + vm_object_reference(shm_handle->shm_object); + rv = vm_map_find(&p->p_vmspace->vm_map, shm_handle->shm_object, + 0, &attach_va, size, (flags & MAP_FIXED)?0:1, prot, prot, 0); + if (rv != KERN_SUCCESS) { + error = ENOMEM; + goto done2; + } + vm_map_inherit(&p->p_vmspace->vm_map, + attach_va, attach_va + size, VM_INHERIT_SHARE); + + shmmap_s->va = attach_va; + shmmap_s->shmid = shmid; + shmseg->shm_lpid = p->p_pid; + shmseg->shm_atime = time (NULL); + shmseg->shm_nattch++; + shm_nattch++; + td->td_retval[0] = attach_va; +done2: + mtx_unlock(&Giant); + return (error); +} + +int +shmat(struct thread *td, struct shmat_args *uap) +{ + return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg); +} + +#ifndef __CYGWIN__ +struct oshmid_ds { + struct ipc_perm shm_perm; /* operation perms */ + int shm_segsz; /* size of segment (bytes) */ + u_short shm_cpid; /* pid, creator */ + u_short shm_lpid; /* pid, last operation */ + short shm_nattch; /* no. of current attaches */ + time_t shm_atime; /* last attach time */ + time_t shm_dtime; /* last detach time */ + time_t shm_ctime; /* last change time */ + void *shm_handle; /* internal handle for shm segment */ +}; + +struct oshmctl_args { + int shmid; + int cmd; + struct oshmid_ds *ubuf; +}; + +/* + * MPSAFE + */ +static int +oshmctl(struct thread *td, struct oshmctl_args *uap) +{ +#ifdef COMPAT_43 + int error = 0; + struct shmid_ds *shmseg; + struct oshmid_ds outbuf; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + mtx_lock(&Giant); + shmseg = shm_find_segment_by_shmid(uap->shmid); + if (shmseg == NULL) { + error = EINVAL; + goto done2; + } + switch (uap->cmd) { + case IPC_STAT: + error = ipcperm(td, &shmseg->shm_perm, IPC_R); + if (error) + goto done2; + outbuf.shm_perm = shmseg->shm_perm; + outbuf.shm_segsz = shmseg->shm_segsz; + outbuf.shm_cpid = shmseg->shm_cpid; + outbuf.shm_lpid = shmseg->shm_lpid; + outbuf.shm_nattch = shmseg->shm_nattch; + outbuf.shm_atime = shmseg->shm_atime; + outbuf.shm_dtime = shmseg->shm_dtime; + outbuf.shm_ctime = shmseg->shm_ctime; + outbuf.shm_handle = shmseg->shm_internal; + error = copyout(&outbuf, uap->ubuf, sizeof(outbuf)); + if (error) + goto done2; + break; + default: + /* XXX casting to (sy_call_t *) is bogus, as usual. */ + error = ((sy_call_t *)shmctl)(td, uap); + break; + } +done2: + mtx_unlock(&Giant); + return (error); +#else + return (EINVAL); +#endif +} +#endif /* __CYGWIN__ */ + +#ifndef _SYS_SYSPROTO_H_ +struct shmctl_args { + int shmid; + int cmd; + struct shmid_ds *buf; +}; +#endif + +/* + * MPSAFE + */ +int +kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, size_t *bufsz) +{ + int error = 0; + struct shmid_ds *shmseg; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + + mtx_lock(&Giant); + switch (cmd) { + case IPC_INFO: + memcpy(buf, &shminfo, sizeof(shminfo)); + if (bufsz) + *bufsz = sizeof(shminfo); + td->td_retval[0] = shmalloced; + goto done2; + case SHM_INFO: { + struct shm_info shm_info; + shm_info.used_ids = shm_nused; + shm_info.shm_tot = shm_committed * PAGE_SIZE; +#ifdef __CYGWIN__ + shm_info.shm_atts = shm_nattch; +#else + shm_info.shm_rss = 0; /*XXX where to get from ? */ + shm_info.shm_swp = 0; /*XXX where to get from ? */ + shm_info.swap_attempts = 0; /*XXX where to get from ? */ + shm_info.swap_successes = 0; /*XXX where to get from ? */ +#endif /* __CYGWIN__ */ + memcpy(buf, &shm_info, sizeof(shm_info)); + if (bufsz) + *bufsz = sizeof(shm_info); + td->td_retval[0] = shmalloced; + goto done2; + } + } + if (cmd == SHM_STAT) + shmseg = shm_find_segment_by_shmidx(shmid); + else + shmseg = shm_find_segment_by_shmid(shmid); + if (shmseg == NULL) { + error = EINVAL; + goto done2; + } + switch (cmd) { + case SHM_STAT: + case IPC_STAT: + error = ipcperm(td, &shmseg->shm_perm, IPC_R); + if (error) + goto done2; + memcpy(buf, shmseg, sizeof(struct shmid_ds)); + if (bufsz) + *bufsz = sizeof(struct shmid_ds); + if (cmd == SHM_STAT) + td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->shm_perm); + break; + case IPC_SET: { + struct shmid_ds *shmid; + + shmid = (struct shmid_ds *)buf; + error = ipcperm(td, &shmseg->shm_perm, IPC_M); + if (error) + goto done2; + shmseg->shm_perm.uid = shmid->shm_perm.uid; + shmseg->shm_perm.gid = shmid->shm_perm.gid; + shmseg->shm_perm.mode = + (shmseg->shm_perm.mode & ~ACCESSPERMS) | + (shmid->shm_perm.mode & ACCESSPERMS); + shmseg->shm_ctime = time (NULL); + break; + } + case IPC_RMID: + error = ipcperm(td, &shmseg->shm_perm, IPC_M); + if (error) + goto done2; + shmseg->shm_perm.key = IPC_PRIVATE; + shmseg->shm_perm.mode |= SHMSEG_REMOVED; + if (shmseg->shm_nattch <= 0) { + shm_deallocate_segment(shmseg); + shm_last_free = IPCID_TO_IX(shmid); + } + break; +#if 0 + case SHM_LOCK: + case SHM_UNLOCK: +#endif + default: + error = EINVAL; + break; + } +done2: + mtx_unlock(&Giant); + return (error); +} + +int +shmctl(struct thread *td, struct shmctl_args *uap) +{ + int error = 0; + struct shmid_ds buf; + size_t bufsz; + + /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ + if (uap->cmd == IPC_SET) { + if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) + goto done; + } +#ifdef __CYGWIN__ + if (uap->cmd == IPC_INFO && uap->shmid > 0) { + /* Can't use the default kern_shmctl interface. */ + int shmid = uap->shmid; + if (shmid > shminfo.shmmni) + shmid = shminfo.shmmni; + error = copyout(shmsegs, uap->buf, + shmid * sizeof(struct shmid_ds)); + td->td_retval[0] = error ? -1 : 0; + return (error); + } +#endif /* __CYGWIN__ */ + + error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); + if (error) + goto done; + + /* Cases in which we need to copyout */ + switch (uap->cmd) { + case IPC_INFO: + case SHM_INFO: + case SHM_STAT: + case IPC_STAT: + error = copyout(&buf, uap->buf, bufsz); + break; + } + +done: + if (error) { + /* Invalidate the return value */ + td->td_retval[0] = -1; + } + return (error); +} + + +#ifndef _SYS_SYSPROTO_H_ +struct shmget_args { + key_t key; + size_t size; + int shmflg; +}; +#endif + +static int +shmget_existing(struct thread *td, struct shmget_args *uap, int mode, int segnum) +{ + struct shmid_ds *shmseg; + int error; + + shmseg = &shmsegs[segnum]; + if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { + /* + * This segment is in the process of being allocated. Wait + * until it's done, and look the key up again (in case the + * allocation failed or it was freed). + */ + shmseg->shm_perm.mode |= SHMSEG_WANTED; + error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0); + if (error) + return (error); + return (EAGAIN); + } + if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) + return (EEXIST); + error = ipcperm(td, &shmseg->shm_perm, mode); + if (error) + return (error); + if (uap->size && uap->size > shmseg->shm_segsz) + return (EINVAL); + td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); +#ifdef __CYGWIN__ + td->td_retval[1] = + vm_object_duplicate(td, shmseg->shm_internal->shm_object); +#endif /* __CYGWIN__ */ + return (0); +} + +static int +shmget_allocate_segment(struct thread *td, struct shmget_args *uap, int mode) +{ + int i, segnum, shmid, size; +#ifndef __CYGWIN__ + struct ucred *cred = td->td_ucred; +#endif /* __CYGWIN__ */ + struct shmid_ds *shmseg; + struct shm_handle *shm_handle; + + GIANT_REQUIRED; + + if (uap->size < (unsigned long) shminfo.shmmin || + uap->size > (unsigned long) shminfo.shmmax) + return (EINVAL); + if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ + return (ENOSPC); + size = round_page(uap->size); + if (shm_committed + btoc(size) > (unsigned long) shminfo.shmall) + return (ENOMEM); + if (shm_last_free < 0) { + shmrealloc(); /* Maybe expand the shmsegs[] array. */ + for (i = 0; i < shmalloced; i++) + if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) + break; + if (i == shmalloced) + return (ENOSPC); + segnum = i; + } else { + segnum = shm_last_free; + shm_last_free = -1; + } + shmseg = &shmsegs[segnum]; + /* + * In case we sleep in malloc(), mark the segment present but deleted + * so that noone else tries to create the same key. + */ + shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; + shmseg->shm_perm.key = uap->key; + shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; + shm_handle = (struct shm_handle *) + sys_malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); + shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); + + /* + * We make sure that we have allocated a pager before we need + * to. + */ + if (shm_use_phys) { + shm_handle->shm_object = + vm_pager_allocate(OBJT_PHYS, 0, size, VM_PROT_DEFAULT, 0); + } else { + shm_handle->shm_object = + vm_pager_allocate(OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0); + } + VM_OBJECT_LOCK(shm_handle->shm_object); + vm_object_clear_flag(shm_handle->shm_object, OBJ_ONEMAPPING); + vm_object_set_flag(shm_handle->shm_object, OBJ_NOSPLIT); + VM_OBJECT_UNLOCK(shm_handle->shm_object); + + shmseg->shm_internal = shm_handle; +#ifdef __CYGWIN__ + shmseg->shm_perm.cuid = shmseg->shm_perm.uid = td->ipcblk->uid; + shmseg->shm_perm.cgid = shmseg->shm_perm.gid = td->ipcblk->gid; +#else + shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; + shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; +#endif /* __CYGWIN__ */ + shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | + (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; + shmseg->shm_segsz = uap->size; + shmseg->shm_cpid = td->td_proc->p_pid; + shmseg->shm_lpid = shmseg->shm_nattch = 0; + shmseg->shm_atime = shmseg->shm_dtime = 0; + shmseg->shm_ctime = time (NULL); + shm_committed += btoc(size); + shm_nused++; + if (shmseg->shm_perm.mode & SHMSEG_WANTED) { + /* + * Somebody else wanted this key while we were asleep. Wake + * them up now. + */ + shmseg->shm_perm.mode &= ~SHMSEG_WANTED; + wakeup(shmseg); + } + td->td_retval[0] = shmid; +#ifdef __CYGWIN__ + td->td_retval[1] = + vm_object_duplicate(td, shmseg->shm_internal->shm_object); +#endif /* __CYGWIN__ */ + return (0); +} + +/* + * MPSAFE + */ +int +shmget(struct thread *td, struct shmget_args *uap) +{ + int segnum, mode; + int error; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + mtx_lock(&Giant); + mode = uap->shmflg & ACCESSPERMS; + if (uap->key != IPC_PRIVATE) { + again: + segnum = shm_find_segment_by_key(uap->key); + if (segnum >= 0) { + error = shmget_existing(td, uap, mode, segnum); + if (error == EAGAIN) + goto again; + goto done2; + } + if ((uap->shmflg & IPC_CREAT) == 0) { + error = ENOENT; + goto done2; + } + } + error = shmget_allocate_segment(td, uap, mode); +done2: +#ifdef __CYGWIN__ + if (!error) + ipcexit_creat_hookthread (td); +#endif + mtx_unlock(&Giant); + return (error); +} + +#ifndef __CYGWIN__ +/* + * MPSAFE + */ +int +shmsys(td, uap) + struct thread *td; + /* XXX actually varargs. */ + struct shmsys_args /* { + int which; + int a2; + int a3; + int a4; + } */ *uap; +{ + int error; + + if (!jail_sysvipc_allowed && jailed(td->td_ucred)) + return (ENOSYS); + if (uap->which < 0 || + uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) + return (EINVAL); + mtx_lock(&Giant); + error = (*shmcalls[uap->which])(td, &uap->a2); + mtx_unlock(&Giant); + return (error); +} +#endif /* __CYGWIN__ */ + +static void +shmfork_myhook(struct proc *p1, struct proc *p2) +{ + struct shmmap_state *shmmap_s; + size_t size; + int i; + + size = shminfo.shmseg * sizeof(struct shmmap_state); + shmmap_s = (struct shmmap_state *) sys_malloc(size, M_SHM, M_WAITOK); + bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); + p2->p_vmspace->vm_shm = shmmap_s; + for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) + if (shmmap_s->shmid != -1) { + shm_nattch++; + shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; + } +} + +#ifdef __CYGWIN__ +int cygwin_shmfork_myhook (struct thread *td, struct proc *parent) +{ + ipcexit_creat_hookthread (td); + ipc_p_vmspace (td->ipcblk); + ipc_p_vmspace (parent); + shmfork_myhook (parent, td->ipcblk); + return 0; +} +#endif + +void +shmexit_myhook(struct vmspace *vm) +{ + struct shmmap_state *base, *shm; + int i; + + GIANT_REQUIRED; + + if ((base = vm->vm_shm) != NULL) { + vm->vm_shm = NULL; + for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { + if (shm->shmid != -1) + shm_delete_mapping(vm, shm); + } + sys_free(base, M_SHM); + } +} + +static void +shmrealloc(void) +{ + int i; + struct shmid_ds *newsegs; + + if (shmalloced >= shminfo.shmmni) + return; + + newsegs = (struct shmid_ds *) sys_malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK); + if (newsegs == NULL) + return; + for (i = 0; i < shmalloced; i++) + bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); + for (; i < shminfo.shmmni; i++) { + shmsegs[i].shm_perm.mode = SHMSEG_FREE; + shmsegs[i].shm_perm.seq = 0; + } + sys_free(shmsegs, M_SHM); + shmsegs = newsegs; + shmalloced = shminfo.shmmni; +} + +void +shminit(void) +{ + int i; + + TUNABLE_INT_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall); + for (i = PAGE_SIZE; i > 0; i--) { + shminfo.shmmax = shminfo.shmall * PAGE_SIZE; + if (shminfo.shmmax >= shminfo.shmall) + break; + } + TUNABLE_INT_FETCH("kern.ipc.shmmin", &shminfo.shmmin); + TUNABLE_INT_FETCH("kern.ipc.shmmni", &shminfo.shmmni); + TUNABLE_INT_FETCH("kern.ipc.shmseg", &shminfo.shmseg); + TUNABLE_INT_FETCH("kern.ipc.shm_use_phys", &shm_use_phys); + + shmalloced = shminfo.shmmni; + shmsegs = (struct shmid_ds *) sys_malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK); + if (shmsegs == NULL) + panic("cannot allocate initial memory for sysvshm"); + for (i = 0; i < shmalloced; i++) { + shmsegs[i].shm_perm.mode = SHMSEG_FREE; + shmsegs[i].shm_perm.seq = 0; + } + shm_last_free = 0; + shm_nused = 0; + shm_committed = 0; +#ifndef __CYGWIN__ + shmexit_hook = &shmexit_myhook; + shmfork_hook = &shmfork_myhook; +#endif /* __CYGWIN__ */ +} + +int +shmunload(void) +{ + + if (shm_nused > 0) + return (EBUSY); + + sys_free(shmsegs, M_SHM); +#ifndef __CYGWIN__ + shmexit_hook = NULL; + shmfork_hook = NULL; +#endif /* __CYGWIN__ */ + return (0); +} + +#ifndef __CYGWIN__ +static int +sysctl_shmsegs(SYSCTL_HANDLER_ARGS) +{ + + return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0]))); +} + +static int +sysvshm_modload(struct module *module, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + shminit(); + break; + case MOD_UNLOAD: + error = shmunload(); + break; + case MOD_SHUTDOWN: + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static moduledata_t sysvshm_mod = { + "sysvshm", + &sysvshm_modload, + NULL +}; + +SYSCALL_MODULE_HELPER(shmsys); +SYSCALL_MODULE_HELPER(shmat); +SYSCALL_MODULE_HELPER(shmctl); +SYSCALL_MODULE_HELPER(shmdt); +SYSCALL_MODULE_HELPER(shmget); + +DECLARE_MODULE(sysvshm, sysvshm_mod, + SI_SUB_SYSV_SHM, SI_ORDER_FIRST); +MODULE_VERSION(sysvshm, 1); +#endif /* __CYGWIN__ */ +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/threaded_queue.cc b/winsup/cygserver/threaded_queue.cc index ba0fe4178..53dd6fa11 100644 --- a/winsup/cygserver/threaded_queue.cc +++ b/winsup/cygserver/threaded_queue.cc @@ -1,6 +1,6 @@ /* threaded_queue.cc - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Robert Collins @@ -10,6 +10,7 @@ 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 @@ -73,7 +74,7 @@ threaded_queue::~threaded_queue () { queue_request *const ptr = reqptr; reqptr = reqptr->_next; - safe_delete (ptr); + delete ptr; } DeleteCriticalSection (&_queue_lock); @@ -267,7 +268,7 @@ threaded_queue::worker_loop () assert (reqptr); reqptr->process (); - safe_delete (reqptr); + delete reqptr; } } @@ -406,3 +407,4 @@ queue_submission_loop::start_routine (const LPVOID lpParam) } /*****************************************************************************/ +#endif /* __OUTSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/transport.cc b/winsup/cygserver/transport.cc index 2c7100d77..a52bb60e7 100644 --- a/winsup/cygserver/transport.cc +++ b/winsup/cygserver/transport.cc @@ -1,6 +1,6 @@ -/* cygserver_transport.cc +/* transport.cc - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Robert Collins @@ -19,31 +19,33 @@ details. */ #include -#include "safe_memory.h" - -#include "cygserver_transport.h" -#include "cygserver_transport_pipes.h" -#include "cygserver_transport_sockets.h" +#include "transport.h" +#include "transport_pipes.h" +#include "transport_sockets.h" /* The factory */ transport_layer_base * create_server_transport () { if (wincap.is_winnt ()) - return safe_new0 (transport_layer_pipes); + return new transport_layer_pipes; else - return safe_new0 (transport_layer_sockets); + return new transport_layer_sockets; } #ifndef __INSIDE_CYGWIN__ -void +bool transport_layer_base::impersonate_client () -{} +{ + return true; +} -void +bool transport_layer_base::revert_to_self () -{} +{ + return true; +} #endif /* !__INSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/cygserver_transport.h b/winsup/cygserver/transport.h similarity index 75% rename from winsup/cygserver/cygserver_transport.h rename to winsup/cygserver/transport.h index 915f35e66..76018d20e 100644 --- a/winsup/cygserver/cygserver_transport.h +++ b/winsup/cygserver/transport.h @@ -1,6 +1,6 @@ -/* cygserver_transport.h +/* transport.h - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Robert Collins @@ -10,8 +10,8 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef _CYGSERVER_TRANSPORT_ -#define _CYGSERVER_TRANSPORT_ +#ifndef _TRANSPORT_H +#define _TRANSPORT_H class transport_layer_base *create_server_transport (); @@ -29,11 +29,11 @@ public: virtual int connect () = 0; #ifndef __INSIDE_CYGWIN__ - virtual void impersonate_client (); - virtual void revert_to_self (); + virtual bool impersonate_client (); + virtual bool revert_to_self (); #endif virtual ~transport_layer_base (); }; -#endif /* _CYGSERVER_TRANSPORT_ */ +#endif /* _TRANSPORT_H */ diff --git a/winsup/cygserver/transport_pipes.cc b/winsup/cygserver/transport_pipes.cc index 5fd11581d..5f8ceec4d 100644 --- a/winsup/cygserver/transport_pipes.cc +++ b/winsup/cygserver/transport_pipes.cc @@ -1,6 +1,6 @@ -/* cygserver_transport_pipes.cc +/* transport_pipes.cc - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Robert Collins @@ -25,11 +25,14 @@ details. */ #include #include "cygerrno.h" -#include "cygserver_transport.h" -#include "cygserver_transport_pipes.h" +#include "transport.h" +#include "transport_pipes.h" #ifndef __INSIDE_CYGWIN__ #include "cygserver.h" +#include "cygserver_ipc.h" +#else +#include "security.h" #endif enum @@ -64,7 +67,6 @@ transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe) assert (_hPipe); assert (_hPipe != INVALID_HANDLE_VALUE); - init_security (); } #endif /* !__INSIDE_CYGWIN__ */ @@ -75,22 +77,6 @@ transport_layer_pipes::transport_layer_pipes () _is_accepted_endpoint (false), _is_listening_endpoint (false) { - init_security (); -} - -void -transport_layer_pipes::init_security () -{ - assert (wincap.has_security ()); - - /* FIXME: pthread_once or equivalent needed */ - - InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE); - - _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); - _sec_all_nih.lpSecurityDescriptor = &_sd; - _sec_all_nih.bInheritHandle = FALSE; } transport_layer_pipes::~transport_layer_pipes () @@ -138,7 +124,7 @@ transport_layer_pipes::accept (bool *const recoverable) (PIPE_TYPE_BYTE | PIPE_WAIT), PIPE_UNLIMITED_INSTANCES, 0, 0, 1000, - &_sec_all_nih); + &sec_all_nih); const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE && pipe_instance == 0 @@ -175,7 +161,7 @@ transport_layer_pipes::accept (bool *const recoverable) return NULL; } - return safe_new (transport_layer_pipes, accept_pipe); + return new transport_layer_pipes (accept_pipe); } #endif /* !__INSIDE_CYGWIN__ */ @@ -281,7 +267,7 @@ transport_layer_pipes::connect () _hPipe = CreateFile (_pipe_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - &_sec_all_nih, + &sec_all_nih, OPEN_EXISTING, SECURITY_IMPERSONATION, NULL); @@ -331,32 +317,33 @@ transport_layer_pipes::connect () #ifndef __INSIDE_CYGWIN__ -void +bool transport_layer_pipes::impersonate_client () { assert (_hPipe); assert (_hPipe != INVALID_HANDLE_VALUE); assert (_is_accepted_endpoint); - // verbose: debug_printf ("impersonating pipe %p", _hPipe); - if (_hPipe) + if (_hPipe && !ImpersonateNamedPipeClient (_hPipe)) { - assert (_hPipe != INVALID_HANDLE_VALUE); - - if (!ImpersonateNamedPipeClient (_hPipe)) - debug_printf ("Failed to Impersonate the client, (%lu)", - GetLastError ()); + debug_printf ("Failed to Impersonate client, (%lu)", GetLastError ()); + return false; } - // verbose: debug_printf ("I am who you are"); + + return true; } -void +bool transport_layer_pipes::revert_to_self () { assert (_is_accepted_endpoint); - RevertToSelf (); - // verbose: debug_printf ("I am who I yam"); + if (!RevertToSelf ()) + { + debug_printf ("Failed to RevertToSelf, (%lu)", GetLastError ()); + return false; + } + return true; } #endif /* !__INSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/cygserver_transport_pipes.h b/winsup/cygserver/transport_pipes.h similarity index 67% rename from winsup/cygserver/cygserver_transport_pipes.h rename to winsup/cygserver/transport_pipes.h index 4bea2eb13..7265a88f8 100644 --- a/winsup/cygserver/cygserver_transport_pipes.h +++ b/winsup/cygserver/transport_pipes.h @@ -1,6 +1,6 @@ -/* cygserver_transport_pipes.h +/* transport_pipes.h - Copyright 2001, 2002 Red Hat Inc. + Copyright 2001, 2002, 2003 Red Hat Inc. Written by Robert Collins @@ -10,8 +10,8 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef _CYGSERVER_TRANSPORT_PIPES_ -#define _CYGSERVER_TRANSPORT_PIPES_ +#ifndef _TRANSPORT_PIPES_H +#define _TRANSPORT_PIPES_H /* Named pipes based transport, for security on NT */ class transport_layer_pipes : public transport_layer_base @@ -28,20 +28,14 @@ public: virtual int connect (); #ifndef __INSIDE_CYGWIN__ - virtual void impersonate_client (); - virtual void revert_to_self (); + virtual bool impersonate_client (); + virtual bool revert_to_self (); #endif transport_layer_pipes (); virtual ~transport_layer_pipes (); private: - /* for pipe based communications */ - void init_security (); - - //FIXME: allow inited, sd, all_nih_.. to be static members - SECURITY_DESCRIPTOR _sd; - SECURITY_ATTRIBUTES _sec_all_nih; const char *const _pipe_name; HANDLE _hPipe; const bool _is_accepted_endpoint; @@ -50,4 +44,4 @@ private: transport_layer_pipes (HANDLE hPipe); }; -#endif /* _CYGSERVER_TRANSPORT_PIPES_ */ +#endif /* _TRANSPORT_PIPES_H */ diff --git a/winsup/cygserver/transport_sockets.cc b/winsup/cygserver/transport_sockets.cc index 78d237adc..f3668f6d8 100644 --- a/winsup/cygserver/transport_sockets.cc +++ b/winsup/cygserver/transport_sockets.cc @@ -1,4 +1,4 @@ -/* cygserver_transport_sockets.cc +/* transport_sockets.cc Copyright 2001, 2002 Red Hat Inc. @@ -26,8 +26,8 @@ details. */ #include #include -#include "cygserver_transport.h" -#include "cygserver_transport_sockets.h" +#include "transport.h" +#include "transport_sockets.h" /* to allow this to link into cygwin and the .dll, a little magic is needed. */ #ifndef __OUTSIDE_CYGWIN__ @@ -219,7 +219,7 @@ transport_layer_sockets::accept (bool *const recoverable) debug_printf ("%d = accept () [this = %p, fd = %d]", accept_fd, this, _fd); - return safe_new (transport_layer_sockets, accept_fd); + return new transport_layer_sockets (accept_fd); } #endif /* !__INSIDE_CYGWIN__ */ diff --git a/winsup/cygserver/cygserver_transport_sockets.h b/winsup/cygserver/transport_sockets.h similarity index 86% rename from winsup/cygserver/cygserver_transport_sockets.h rename to winsup/cygserver/transport_sockets.h index d960f9c2c..d684a87c9 100644 --- a/winsup/cygserver/cygserver_transport_sockets.h +++ b/winsup/cygserver/transport_sockets.h @@ -1,4 +1,4 @@ -/* cygserver_transport_sockets.h +/* transport_sockets.h Copyright 2001, 2002 Red Hat Inc. @@ -10,8 +10,8 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef _CYGSERVER_TRANSPORT_SOCKETS_ -#define _CYGSERVER_TRANSPORT_SOCKETS_ +#ifndef _TRANSPORT_SOCKETS_H +#define _TRANSPORT_SOCKETS_H #include #include @@ -43,4 +43,4 @@ private: transport_layer_sockets (int fd); }; -#endif /* _CYGSERVER_TRANSPORT_SOCKETS_ */ +#endif /* _TRANSPORT_SOCKETS_H */ diff --git a/winsup/cygserver/woutsup.h b/winsup/cygserver/woutsup.h index cb67c78d4..39db63997 100644 --- a/winsup/cygserver/woutsup.h +++ b/winsup/cygserver/woutsup.h @@ -1,6 +1,6 @@ /* woutsup.h: for Cygwin code compiled outside the DLL (i.e. cygserver). - Copyright 2002 Red Hat, Inc. + Copyright 2002, 2003 Red Hat, Inc. This file is part of Cygwin. @@ -42,67 +42,32 @@ details. */ #include "wincap.h" +#include "bsd_helper.h" +#include "bsd_log.h" +#include "bsd_mutex.h" + /* The one function we use from winuser.h most of the time */ extern "C" DWORD WINAPI GetLastError (void); extern int cygserver_running; -#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L -#define NEW_MACRO_VARARGS -#endif - -/* - * A reproduction of the macros. This allows code that - * runs both inside and outside the Cygwin DLL to use the same macros - * for logging messages. - */ - -extern "C" void __cygserver__printf (const char *, const char *, ...); - -#ifdef NEW_MACRO_VARARGS - -#define system_printf(...) \ - do \ - { \ - __cygserver__printf (__PRETTY_FUNCTION__, __VA_ARGS__); \ +#define SIGHANDLE(SIG) \ + do \ + { \ + struct sigaction act; \ + \ + act.sa_handler = &handle_signal; \ + act.sa_mask = 0; \ + act.sa_flags = 0; \ + \ + if (sigaction (SIG, &act, NULL) == -1) \ + { \ + panic ("failed to install handler for " #SIG ": %s", \ + strerror (errno)); \ + exit (1); \ + } \ } while (false) -#define __noop_printf(...) do {;} while (false) - -#else /* !NEW_MACRO_VARARGS */ - -#define system_printf(args...) \ - do \ - { \ - __cygserver__printf (__PRETTY_FUNCTION__, ## args); \ - } while (false) - -#define __noop_printf(args...) do {;} while (false) - -#endif /* !NEW_MACRO_VARARGS */ - -#ifdef DEBUGGING -#define debug_printf system_printf -#define paranoid_printf system_printf -#define select_printf system_printf -#define sigproc_printf system_printf -#define syscall_printf system_printf -#define termios_printf system_printf -#define wm_printf system_printf -#define minimal_printf system_printf -#define malloc_printf system_printf -#define thread_printf system_printf -#else -#define debug_printf __noop_printf -#define paranoid_printf __noop_printf -#define select_printf __noop_printf -#define sigproc_printf __noop_printf -#define syscall_printf __noop_printf -#define termios_printf __noop_printf -#define wm_printf __noop_printf -#define minimal_printf __noop_printf -#define malloc_printf __noop_printf -#define thread_printf __noop_printf -#endif - -#include "safe_memory.h" +#define debug_printf(f,...) debug((f),##__VA_ARGS__) +#define syscall_printf(f,...) log(LOG_ERR,(f),##__VA_ARGS__) +#define system_printf(f,...) log(LOG_ERR,(f),##__VA_ARGS__)