/* * PROGRAM: JRD Access Method * MODULE: isc.cpp * DESCRIPTION: General purpose but non-user routines. * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * Added TCP_NO_DELAY option for superserver on Linux * FSG 16.03.2001 * Solaris x86 changes - Konstantin Kuznetsov, Neil McCalden * 26-Sept-2001 Paul Beach - External File Directory Config. Parameter * 17-Oct-2001 Mike Nordell: CPU affinity * 01-Feb-2002 Paul Reeves: Removed hard-coded registry path * * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define * 2002.10.30 Sean Leyne - Code Cleanup, removed obsolete "SUN3_3" port * */ /* $Id: isc.cpp,v 1.51 2004-06-05 09:37:01 robocop Exp $ */ #ifdef DARWIN #define _STLP_CCTYPE #endif #include "firebird.h" #include #include #include #include #include "../jrd/common.h" #include "gen/iberror.h" #include "../jrd/isc.h" #include "../jrd/ibase.h" #include "../jrd/jrd.h" #include "../jrd/scl.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #include "../jrd/jrd_proto.h" /* Temporary file management specific stuff */ #include "../jrd/fil.h" #include "../jrd/dls_proto.h" // I can't find where these are used. //static BOOLEAN dls_init = FALSE; //#if defined(SUPERSERVER) || !defined(SUPERCLIENT) //static BOOLEAN dls_flag = FALSE; //#endif //static BOOLEAN fdls_init = FALSE; //#ifdef SUPERSERVER //static BOOLEAN fdls_flag = FALSE; //#endif /* End of temporary file management specific stuff */ #ifdef SOLARIS #include #endif #ifdef SCO_EV #include #endif /* Win32 specific stuff */ #ifdef WIN_NT #include static USHORT os_type; static SECURITY_ATTRIBUTES security_attr; //static TEXT interbase_directory[MAXPATHLEN]; static bool check_user_privilege(); #endif // WIN_NT static TEXT user_name[256]; static USHORT ast_count; /* VMS Specific Stuff */ #ifdef VMS #include #include #include #include #include #include #include #include "../jrd/lnmdef.h" #include "../jrd/prv_m_bypass.h" #define LOGICAL_NAME_TABLE "LNM$FILE_DEV" #define WAKE_LOCK "gds__process_%d" static POKE pokes; static lock_status wake_lock; #endif /* of ifdef VMS */ #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_CTYPE_H #include #endif /* Unix specific stuff */ #if defined(UNIX) #include #include #include #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #endif #ifndef O_RDWR #include #endif #ifdef VMS static void blocking_ast(void); static void poke_ast(POKE); static int wait_test(SSHORT *); #endif #ifndef REQUESTER void ISC_ast_enter(void) { /************************************** * * I S C _ a s t _ e n t e r * ************************************** * * Functional description * Enter ast mode. * **************************************/ ++ast_count; } #endif #ifndef REQUESTER void ISC_ast_exit(void) { /************************************** * * I S C _ a s t _ e x i t * ************************************** * * Functional description * Enter ast mode. * **************************************/ --ast_count; } #endif bool ISC_check_process_existence(SLONG pid, SLONG xl_pid, bool super_user) { /************************************** * * I S C _ c h e c k _ p r o c e s s _ e x i s t e n c e * ************************************** * * Functional description * Return TRUE if the indicated process * exists. FALSE otherwise. * **************************************/ #if defined(UNIX) return (kill((int) pid, 0) == -1 && (errno == ESRCH || (super_user && errno == EPERM)) ? false : true); #elif defined(VMS) ULONG item = JPI$_PID; return (lib$getjpi(&item, &pid, NULL, NULL, NULL, NULL) == SS$_NONEXPR) ? false : true; #elif defined(WIN_NT) HANDLE handle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, (DWORD) pid); if (!handle && GetLastError() != ERROR_ACCESS_DENIED) { return false; } CloseHandle(handle); return true; #else return true; #endif } #ifdef VMS int ISC_expand_logical_once(const TEXT* file_name, USHORT file_length, TEXT* expanded_name) { /************************************** * * I S C _ e x p a n d _ l o g i c a l _ o n c e * ************************************** * * Functional description * Expand a logical name. If it doesn't exist, return 0. * **************************************/ struct dsc$descriptor_s desc1, desc2; if (!file_length) file_length = strlen(file_name); ISC_make_desc(file_name, &desc1, file_length); ISC_make_desc(LOGICAL_NAME_TABLE, &desc2, sizeof(LOGICAL_NAME_TABLE) - 1); USHORT l; ITM items[2]; items[0].itm_length = 256; items[0].itm_code = LNM$_STRING; items[0].itm_buffer = expanded_name; items[0].itm_return_length = &l; items[1].itm_length = 0; items[1].itm_code = 0; int attr = LNM$M_CASE_BLIND; if (!(sys$trnlnm(&attr, &desc2, &desc1, NULL, items) & 1)) { while (file_length--) *expanded_name++ = *file_name++; *expanded_name = 0; return 0; } expanded_name[l] = 0; return l; } #endif #if (defined SOLARIS || defined SCO_EV) TEXT* ISC_get_host(TEXT* string, USHORT length) { /************************************** * * I S C _ g e t _ h o s t ( S O L A R I S ) * ************************************** * * Functional description * Get host name. * **************************************/ struct utsname name; if (uname(&name) >= 0) strncpy(string, name.nodename, length); else strcpy(string, "local"); return string; } #elif defined (VMS) TEXT* ISC_get_host(TEXT* string, USHORT length) { /************************************** * * I S C _ g e t _ h o s t ( V M S ) * ************************************** * * Functional description * Get host name. * **************************************/ if (!ISC_expand_logical_once("SYS$NODE", sizeof("SYS$NODE") - 1, string)) strcpy(string, "local"); else { TEXT* p = string; if (*p == '_') ++p; for (; *p && *p != ':'; p++) if (*p >= 'A' && *p <= 'Z') *p = *p - 'A' + 'a'; *p = 0; } return string; } #elif defined(WIN_NT) TEXT* ISC_get_host(TEXT* string, USHORT length) { /************************************** * * I S C _ g e t _ h o s t ( W I N _ N T ) * ************************************** * * Functional description * Get host name. * Note that this is not the DNS (TCP/IP) hostname, * it's the Win32 computer name. * **************************************/ DWORD host_len = length; if (GetComputerName(string, &host_len)) { string[host_len] = 0; } else { strcpy(string, "local"); } return string; } #else TEXT* ISC_get_host(TEXT* string, USHORT length) { /************************************** * * I S C _ g e t _ h o s t ( G E N E R I C ) * ************************************** * * Functional description * Get host name. * **************************************/ gethostname(string, length); return string; } #endif #ifdef UNIX int ISC_get_user(TEXT* name, int* id, int* group, TEXT* project, TEXT* organization, int* node, const TEXT* user_string) { /************************************** * * I S C _ g e t _ u s e r ( U N I X ) * ************************************** * * Functional description * Find out who the user is. * **************************************/ /* egid and euid need to be signed, uid_t is unsigned on SUN! */ SLONG egid, euid; TEXT user_name[256]; const TEXT* p = 0; if (user_string && *user_string) { const TEXT* q = user_string; char* un; for (un = user_name; (*un = *q++) && *un != '.'; un++); *un = 0; p = user_name; egid = euid = -1; if (*q) { egid = atoi(q); while (*q && (*q != '.')) q++; if (*q == '.') { q++; euid = atoi(q); } } } else { euid = (SLONG) geteuid(); egid = (SLONG) getegid(); const struct passwd* password = getpwuid(euid); if (password) p = password->pw_name; else p = ""; endpwent(); } if (name) strcpy(name, p); if (id) *id = euid; if (group) *group = egid; if (project) *project = 0; if (organization) *organization = 0; if (node) *node = 0; return (euid == 0); } #endif #ifdef VMS int ISC_get_user( TEXT* name, int* id, int* group, TEXT* project, TEXT* organization, int* node, const TEXT* user_string) { /************************************** * * I S C _ g e t _ u s e r ( V M S ) * ************************************** * * Functional description * Find out who the user is. * **************************************/ SLONG privileges[2]; USHORT uic[2]; TEXT user_name[256]; if (user_string && *user_string) { const TEXT* q = user_string; TEXT* p; for (p = user_name; (*p = *q++) && *p != '.'; p++); *p = 0; uic[0] = uic[1] = -1; if (*q) { uic[1] = atoi(q); while (*q && (*q != '.')) q++; if (*q == '.') { q++; uic[0] = atoi(q); } } privileges[0] = 0; if (name) { for (p = user_name; *p && *p != ' ';) *name++ = *p++; *name = 0; } } else { USHORT len0, len1, len2; ITM items[4]; items[0].itm_code = JPI$_UIC; items[0].itm_length = sizeof(uic); items[0].itm_buffer = uic; items[0].itm_return_length = &len0; items[1].itm_code = JPI$_USERNAME; items[1].itm_length = sizeof(user_name); items[1].itm_buffer = user_name; items[1].itm_return_length = &len1; items[2].itm_code = JPI$_CURPRIV; items[2].itm_length = sizeof(privileges); items[2].itm_buffer = privileges; items[2].itm_return_length = &len2; items[3].itm_code = 0; items[3].itm_length = 0; const SLONG status = sys$getjpiw(NULL, NULL, NULL, items, NULL, NULL, NULL); if (!(status & 1)) { len1 = 0; uic[0] = uic[1] = 0; } user_name[len1] = 0; if (name) { for (const TEXT* p = user_name; *p && *p != ' ';) *name++ = *p++; *name = 0; } } if (id) *id = uic[0]; if (group) *group = uic[1]; if (project) *project = 0; if (organization) *organization = 0; if (node) *node = 0; return (privileges[0] & (SLONG) PRV$M_BYPASS); } #endif #ifdef WIN_NT int ISC_get_user(TEXT* name, int* id, int* group, TEXT* project, TEXT* organization, int* node, const TEXT* user_string) { /************************************** * * I S C _ g e t _ u s e r ( W I N _ N T ) * ************************************** * * Functional description * Find out who the user is. * **************************************/ if (id) *id = -1; if (group) *group = -1; if (project) *project = 0; if (organization) *organization = 0; if (node) *node = 0; if (name) { name[0] = 0; DWORD name_len = 128; if (GetUserName(name, &name_len)) { name[name_len] = 0; /* NT user name is case insensitive */ for (DWORD i = 0; i < name_len; i++) { name[i] = UPPER7(name[i]); } /* This check is not internationalized, the security model needs to be * reengineered, especially on SUPERSERVER where none of these local * user (in process) assumptions are valid. */ if (!strcmp(name, "ADMINISTRATOR")) { if (id) *id = 0; if (group) *group = 0; } } } return check_user_privilege(); } //____________________________________________________________ // // Check to see if the user belongs to the administrator group. // // This routine was adapted from code in routine RunningAsAdminstrator // in \mstools\samples\regmpad\regdb.c. // static bool check_user_privilege() { HANDLE tkhandle; SID_IDENTIFIER_AUTHORITY system_sid_authority = {SECURITY_NT_AUTHORITY}; // First we must open a handle to the access token for this thread. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &tkhandle)) { if (GetLastError() == ERROR_NO_TOKEN) { // If the thread does not have an access token, we'll examine the // access token associated with the process. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tkhandle)) { CloseHandle(tkhandle); return false; } } else { return false; } } TOKEN_GROUPS* ptg = NULL; DWORD token_len = 0; while (true) { /* Then we must query the size of the group information associated with the token. This is guarenteed to fail the first time through because there is no buffer. */ if (GetTokenInformation(tkhandle, TokenGroups, ptg, token_len, &token_len)) { break; } /* If there had been a buffer, it's either too small or something else is wrong. Either way, we can dispose of it. */ if (ptg) { gds__free(ptg); } /* Here we verify that GetTokenInformation failed for lack of a large enough buffer. */ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CloseHandle(tkhandle); return false; } // Allocate a buffer for the group information. ptg = (TOKEN_GROUPS *) gds__alloc((SLONG) token_len); if (!ptg) { CloseHandle(tkhandle); return false; /* NOMEM: */ } // FREE: earlier in this loop, and at procedure return } // Create a System Identifier for the Admin group. PSID admin_sid; if (!AllocateAndInitializeSid(&system_sid_authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_sid)) { gds__free(ptg); CloseHandle(tkhandle); return false; } // Finally we'll iterate through the list of groups for this access // token looking for a match against the SID we created above. bool admin_priv = false; for (DWORD i = 0; i < ptg->GroupCount; i++) { if (EqualSid(ptg->Groups[i].Sid, admin_sid)) { admin_priv = true; break; } } // Deallocate the SID we created. FreeSid(admin_sid); gds__free(ptg); CloseHandle(tkhandle); return admin_priv; } #endif #ifdef VMS int ISC_make_desc(const TEXT* string, struct dsc$descriptor* desc, USHORT length) { /************************************** * * I S C _ m a k e _ d e s c * ************************************** * * Functional description * Make a VMS string descriptor out of a string. Return * length of string. * **************************************/ desc->dsc$b_dtype = DSC$K_DTYPE_T; desc->dsc$b_class = DSC$K_CLASS_S; // CVC: I assume the non-const condition would be needed, can't check. desc->dsc$a_pointer = const_cast(string); if (length) desc->dsc$w_length = length; else { desc->dsc$w_length = 0; while (*string++) ++desc->dsc$w_length; } return desc->dsc$w_length; } #endif SLONG ISC_get_prefix(const TEXT* passed_string) { /************************************** * * i s c _ g e t _ p r e f i x * ************************************** * * Functional description * Parse the 'H' argument further for 'HL' 'HM' or 'H' * **************************************/ const char c = *passed_string; int arg_type; switch (UPPER(c)) { case '\0': arg_type = IB_PREFIX_TYPE; break; case 'L': arg_type = IB_PREFIX_LOCK_TYPE; ++passed_string; break; case 'M': arg_type = IB_PREFIX_MSG_TYPE; ++passed_string; break; default: return (-1); break; } return (gds__get_prefix(arg_type, ++passed_string)); } void ISC_prefix(TEXT* string, const TEXT* root) { /************************************** * * i s c _ $ p r e f i x * ************************************** * * Functional description * Wrapper function for gds__prefix. * **************************************/ gds__prefix(string, root); return; } void ISC_prefix_lock(TEXT* string, const TEXT* root) { /************************************** * * i s c _ $ p r e f i x _ l o c k * ************************************** * * Functional description * Wrapper function for gds__prefix_lock. * **************************************/ gds__prefix_lock(string, root); return; } void ISC_prefix_msg(TEXT* string, const TEXT* root) { /************************************** * * i s c _ $ p r e f i x _ m s g * ************************************** * * Functional description * Wrapper function for gds__prefix_msg. * **************************************/ gds__prefix_msg(string, root); return; } #ifndef REQUESTER void ISC_set_user(const TEXT* string) { /************************************** * * I S C _ s e t _ u s e r * ************************************** * * Functional description * Set user name for feeble operating system that don't * support the concept, or support it badly. * **************************************/ // CVC: And including a buffer overflow, too? // Using static data, not thread safe. Probably deprecated? strncpy(user_name, string, sizeof(user_name)); user_name[sizeof(user_name) - 1] = 0; } #endif #ifdef VMS void ISC_wait(SSHORT * iosb, SLONG event_flag) { /************************************** * * I S C _ w a i t ( V M S ) * ************************************** * * Functional description * Wait for a asynchronous VMS event. * **************************************/ if (*iosb) return; /* If we're at AST state, and we're not running under a AST invoked thread, wait locally. */ if (ast_count && !gds__ast_active()) { sys$synch(event_flag, iosb); return; } gds__thread_wait(wait_test, iosb); } #endif #ifdef VMS void ISC_wake(SLONG process_id) { /************************************** * * I S C _ w a k e * ************************************** * * Functional description * Wake up another VMS process. Process can be local or * remote (but on the same CPU). * **************************************/ /* Try to do a simple wake. If this succeeds, we're done. */ int status = sys$wake(&process_id, 0); #ifdef __ALPHA THREAD_wakeup(); #endif if (status & 1) return; /* Do wake by siezing a lock, delivering a blocking ast */ /* Find a free poke block to use */ POKE poke; for (poke = pokes; poke; poke = poke->poke_next) if (!poke->poke_use_count) break; if (!poke) { poke = (POKE) gds__alloc((SLONG) sizeof(struct poke)); /* FREE: apparently never freed */ if (!poke) { /* NOMEM: */ DEV_REPORT("ISC_wake: out of memory condition"); return; /* error handling too difficult here */ } poke->poke_next = pokes; pokes = poke; } ++poke->poke_use_count; TEXT string[32]; struct dsc$descriptor_s desc; sprintf(string, WAKE_LOCK, process_id); ISC_make_desc(string, &desc, 0); status = sys$enq(0, /* event flag */ LCK$K_PWMODE, /* lock mode */ &poke->poke_lksb, /* Lock status block */ LCK$M_SYSTEM, /* flags */ &desc, /* resource name */ 0, /* parent id */ poke_ast, /* ast address */ poke, /* ast argument */ 0, /* blocking ast */ 0, /* access mode */ 0); } #endif #ifdef VMS void ISC_wake_init(void) { /************************************** * * I S C _ w a k e _ i n i t * ************************************** * * Functional description * Set up to be awakened by another process thru a blocking AST. * **************************************/ TEXT string[32]; struct dsc$descriptor_s desc; /* If we already have lock, we're done */ if (wake_lock.lksb_lock_id) return; sprintf(string, WAKE_LOCK, getpid()); ISC_make_desc(string, &desc, 0); int status = sys$enqw(0, /* event flag */ LCK$K_PWMODE, /* lock mode */ &wake_lock, /* Lock status block */ LCK$M_SYSTEM, /* flags */ &desc, /* resource name */ 0, /* parent id */ 0, /* ast address */ 0, /* ast argument */ blocking_ast, /* blocking ast */ 0, /* access mode */ 0); } #endif #ifdef VMS static void blocking_ast(void) { /************************************** * * b l o c k i n g _ a s t * ************************************** * * Functional description * Somebody else is trying to post a lock. * **************************************/ /* Initially down grade the lock to let the other guy complete */ int status = sys$enqw(0, /* event flag */ LCK$K_NLMODE, /* lock mode */ &wake_lock, /* Lock status block */ LCK$M_CONVERT, /* flags */ 0, /* resource name */ 0, /* parent id */ 0, /* ast address */ 0, /* ast argument */ 0, /* blocking ast */ 0, /* access mode */ 0); /* Wake us up */ status = sys$wake(0, 0); /* Now try to upgrade lock */ status = sys$enq(0, /* event flag */ LCK$K_PRMODE, /* lock mode */ &wake_lock, /* Lock status block */ LCK$M_CONVERT, /* flags */ 0, /* resource name */ 0, /* parent id */ 0, /* ast address */ 0, /* ast argument */ blocking_ast, /* blocking ast */ 0, /* access mode */ 0); } #endif #ifdef VMS static void poke_ast(POKE poke) { /************************************** * * p o k e _ a s t * ************************************** * * Functional description * Lock to poke event has completed. Update value block * and deque the lock. * **************************************/ lock_status* lksb = &poke->poke_lksb; int status = sys$deq(lksb->lksb_lock_id, 0, 0, 0); --poke->poke_use_count; } #endif #ifdef VMS static int wait_test(SSHORT * iosb) { /************************************** * * w a i t _ t e s t * ************************************** * * Functional description * Return true if an I/O event is complete. * **************************************/ return *iosb; } #endif #ifdef UNIX #ifdef AIX_PPC #define _UNIX95 #endif #include #ifdef AIX_PPC #undef _UNIX95 #endif SLONG ISC_get_user_group_id(const TEXT* user_group_name) { /************************************** * * I S C _ g e t _ u s e r _ g r o u p _ n a n e * ************************************** * * Functional description * * Return user group id if user group name found, * Otherwise return 0. * * --- for UNIX platform --- * **************************************/ SLONG n = 0; const struct group* user_group = getgrnam(user_group_name); if (user_group) n = user_group->gr_gid; return (n); } #else SLONG ISC_get_user_group_id(const TEXT* user_group_name) { /************************************** * * I S C _ g e t _ u s e r _ g r o u p _ n a n e * * --- for non-UNIX platform --- * ************************************** * * Functional description * * needed to be developed for non-unix platform * * Currently it just returns 0. * **************************************/ SLONG n = 0; return (n); } #endif /* end of ifdef UNIX */ #ifdef WIN_NT // Returns the type of OS: true for NT, // false for the 16-bit based ones (9x/ME, ...). // bool ISC_is_WinNT() { // thread safe??? :-) if (!os_type) { os_type = 1; /* Default to NT */ /* The first time this routine is called we use the Windows API call GetVersion to determine whether Windows/NT or Chicago is running. */ OSVERSIONINFO OsVersionInfo; OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx((LPOSVERSIONINFO) & OsVersionInfo)) { if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) os_type = 2; } } return (os_type != 2); } //____________________________________________________________ // // LPSECURITY_ATTRIBUTES ISC_get_security_desc() { if (security_attr.lpSecurityDescriptor) { return &security_attr; } PSECURITY_DESCRIPTOR p_security_desc = (PSECURITY_DESCRIPTOR) gds__alloc((SLONG) SECURITY_DESCRIPTOR_MIN_LENGTH); /* FREE: apparently never freed */ if (!p_security_desc) /* NOMEM: */ { return NULL; } #ifdef DEBUG_GDS_ALLOC gds_alloc_flag_unfreed((void *) p_security_desc); #endif if (!InitializeSecurityDescriptor( p_security_desc, SECURITY_DESCRIPTOR_REVISION) || !SetSecurityDescriptorDacl(p_security_desc, TRUE, NULL, FALSE)) { gds__free(p_security_desc); return NULL; } security_attr.nLength = sizeof(security_attr); security_attr.lpSecurityDescriptor = p_security_desc; security_attr.bInheritHandle = TRUE; return &security_attr; } #endif