/* ** Name: clean_channels - garbage collect channels to dead processes ** ** Description: ** Called whenever we're about to assign a channel to a new process. ** Before assigning the new channel, we compact the existing channels ** array by reclaiming any channels to old, dead processes. ** ** Inputs: ** None ** ** Outputs: ** None ** ** Returns: ** STATUS ** ** History: ** 23-may-1994 (bryanp) ** Created as part of resolving B60736. ** 08-Nov-2007 (jonj) ** Check that PID is already known to be dead. */ static STATUS clean_channels(void) { i4 i; i4 j; i4 s; char msg_buf[250]; CL_ERR_DESC local_sys_err; CP_CHANNEL *CPchan; for (i = 0; i < cpres_num_channels_assigned; i++) { CPchan = &cpres_channels[i]; if ( CPchan->state == CPchanIsDead || !(PCis_alive(CPchan->pid)) ) { TRdisplay("%@ cscpres: garbage collect channel to %x\n", CPchan->pid); /* ** What cleanup is required: ** 1) cancel any outstanding I/O on this channel ** 2) deassign the channel ** 3) shift all the other channels down one in the array ** (ugh, but we don't do this very often) ** 4) back 'i' off by one so that we don't skip any ** channels in the loop ** 5) back cpres_num_channels_assigned off by one to ** reflect the cleaned-up channel. */ /* ** If already known to be dead, we've cancelled/deassigned ** already. */ if ( CPchan->state != CPchanIsDead ) { CPchan->state = CPchanIsDead; s = sys$cancel(CPchan->chan); if ( s != SS$_NORMAL ) { STprintf(msg_buf, "Error %x cancelling channel %x", s, CPchan->chan); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); TRdisplay("%s\n", msg_buf); return (FAIL); } s = sys$dassgn(CPchan->chan); if ( s != SS$_NORMAL ) { STprintf(msg_buf, "Error %x releasing channel %x", s, CPchan->chan); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); TRdisplay("%s\n", msg_buf); return (FAIL); } } for (j = i + 1; j < cpres_num_channels_assigned; j++) { cpres_channels[j - 1].pid = cpres_channels[j].pid; cpres_channels[j - 1].state = cpres_channels[j].state; cpres_channels[j - 1].chan = cpres_channels[j].chan; } i--; cpres_num_channels_assigned--; } } return (OK); }
/* ** Name: CScp_resume - Resume (a thread in) a process ** ** Description: ** This routine resumes the indicated thread in the indicated process. ** ** If the indicated process is this process, then this is a simple ** CSresume operation. If the indicated process is another process, then ** that process must be notified that it should CSresume the indicated ** thread. ** ** Inputs: ** cpid pointer to CS_CPID with ** .pid - the indicated process ** .sid - the indicated session ** .iosb - a thread-safe IOSB ** .data - where we'll place a pointer ** to the CPchan written to. ** ** Outputs: ** None ** ** Returns: ** void ** ** History: ** Summer, 1992 (bryanp) ** Working on the new portable logging and locking system. ** 9-oct-1992 (bryanp) ** Use global IOSB, not stack IOSB, so that when QIO completes ** some time from now it will not overwrite arbitrary stack stuff. ** 19-oct-1992 (bryanp) ** CSresume expects to be called at AST level. Oblige it by invoking it ** via sys$dclast(). ** 20-oct-1992 (bryanp) ** Back out the DCLAST change; CSresume can now be called at normal ** level. ** 14-dec-1992 (bryanp) ** ERsend() calls should be ERlog() calls. ** 29-sep-1993 (walt) ** Get an event flag number from lib$get_ef rather than use event flag ** zero in the sys$qio call. ** 18-oct-1993 (rachael) ** Call lib$signal(SS$_DEBUG) only when compiled with xDEBUG flag. ** 16-Nov-1998 (jenjo02) ** Prototype changed to pass CS_CPID * instead of PID, SID. ** 08-Nov-2007 (jonj) ** Write with IO$M_NOW and IO$M_READERCHECK, check for dead reader ** process (NOREADER), mark this PID/channel as dead for subsequent ** resumers to ignore. IO$M_NOW does not wait for the reader to ** read. ** 04-Apr-2008 (jonj) ** Embed IOSB in CS_CPID and reinstate lib$get_ef() to assure ** thread-safeness. ** Disable/reenable ASTs to prevent seen duplicate reads on the ** other end. ** Supply cpres_mbx_write_complete() AST to check IOSB status ** for NOREADER. */ void CScp_resume( CS_CPID *cpid ) { i4 vms_status; CS_CP_WAKEUP_MSG wakeup_msg; i4 mbox_chan; char msg_buf[100]; CL_ERR_DESC local_sys_err; CP_CHANNEL *CPchan; II_VMS_EF_NUMBER efn; i4 ReenableASTs; if (cpid->pid == Cs_srv_block.cs_pid) { CSresume(cpid->sid); } else { /* Disable AST delivery */ ReenableASTs = (sys$setast(0) == SS$_WASSET); /* Initialize to success */ vms_status = SS$_NORMAL; if ( cpres_mbx_assign(cpid->pid, &CPchan) == OK ) { /* If reader is not alive, do nothing */ if ( CPchan->state == CPchanIsAlive ) { /* The SID of the session to resume */ wakeup_msg.wakeup_sid = cpid->sid; /* horda03 - Fill in details to help track Cross-Process ** ACCVIO problem. */ wakeup_msg.wakeup_pid = cpid->pid; wakeup_msg.from_pid = Cs_srv_block.cs_pid; /* If from AST, "from_sid" is meaningless */ if ( (wakeup_msg.sender_in_ast = lib$ast_in_prog()) ) wakeup_msg.from_sid = 0; else wakeup_msg.from_sid = (CS_SID)Cs_srv_block.cs_current; /* ** Plunk message, don't wait for reader to read it. ** ** Use IOSB embedded in CS_CPID, pass CS_CPID* to ** AST completion. */ /* Set CPchan in the CS_CPID for AST's use */ cpid->data = (PTR)CPchan; vms_status = sys$qio(EFN$C_ENF, CPchan->chan, IO$_WRITEVBLK | IO$M_NOW | IO$M_READERCHECK, &cpid->iosb, cpres_mbx_write_complete, cpid, &wakeup_msg, sizeof(wakeup_msg), 0, 0, 0, 0); if ( vms_status != SS$_NORMAL ) { STprintf(msg_buf, "[%x.%x] Error (%x) queueing write to %x on channel %d", wakeup_msg.from_pid, wakeup_msg.from_sid, vms_status, CPchan->pid, CPchan->chan); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); } } } else { STprintf(msg_buf, "Unable to assign channel to %x", cpid->pid); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); STprintf(msg_buf, "Ignoring error in assigning mailbox for PID %x", cpid->pid); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); /* ** The process we were going to send this message to will probably ** "hang", which at least allows some sort of diagnosis. Killing ** ourselves at this point is less useful, since it tends to crash ** the entire installation. */ } if ( vms_status != SS$_NORMAL ) { STprintf(msg_buf, "CScp_resume QIO to %x failed with status %x", cpid->pid, vms_status); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); #ifdef xDEBUG lib$signal(SS$_DEBUG); #endif PCexit(FAIL); } if ( ReenableASTs ) sys$setast(1); } return; }
/* ** Name: cpres_mbx_assign - get channel to target process, assign if need ** ** Description: ** This subroutine looks up the channel to the target process. If we have ** not yet assigned a channel to the target process, then we assign one ** and remember it. The resulting channel is returned. ** ** Inputs: ** pid - the target process's pid ** ** Outputs: ** CPchan - Pointer to CP_CHANNEL of process. ** ** Returns: ** OK, !OK ** ** History: ** Summer, 1992 (bryanp) ** Working on the new portable logging and locking system. ** 14-dec-1992 (bryanp) ** ERsend() calls should be ERlog() calls. ** 23-may-1994 (bryanp) B60736 ** Added clean_channels() routine and called it periodically to ** ensure that a process doesn't accumulate channels to dead ** processes indefinitely. ** 02-May-2007 (jonj) ** Use of CSp/v_semaphore is prohibited from within an AST as ** it corrupts cs_inkernel and the ready queues. Use CS_ASET/ACLR ** instead. ** 08-Nov-2007 (jonj) ** Changed function to return pointer to CP_CHANNEL instead ** of channel number, initialize IsDead to FALSE. ** 04-Apr-2008 (jonj) ** Remove disabling of ASTs here, caller will have already done ** that. ** Check if PID has been deadified by write AST; if so, ** cancel any pending I/O, deassign channel, mark CPchan ** as unusuable (dead). */ static STATUS cpres_mbx_assign(PID pid, CP_CHANNEL **CPchan) { i4 i; struct dsc$descriptor_s name_desc; i4 vms_status; char mbx_name[100]; char *inst_id; char msg_buf[250]; STATUS status; STATUS cl_status; PID my_pid; CL_ERR_DESC local_sys_err; CP_CHANNEL *NewCPchan; /* Loop until we get the sem */ while ( !CS_TAS(&cpres_channels_sem) ); for (i = 0; i < cpres_num_channels_assigned; i++) { if (cpres_channels[i].pid == pid) { /* ** If write completion AST noticed that ** the reading process has died or gone away, ** mark its PID as dead, cancel any leftover ** I/O and deassign the channel. */ if ( cpres_channels[i].state == CPchanIsDying ) { sys$cancel(cpres_channels[i].chan); sys$dassgn(cpres_channels[i].chan); cpres_channels[i].state = CPchanIsDead; STprintf(msg_buf, "%x PID %x on channel %d is dead, deassigned", Cs_srv_block.cs_pid, cpres_channels[i].pid, cpres_channels[i].chan); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); } /* Return channel, dead or alive */ *CPchan = &cpres_channels[i]; CS_ACLR(&cpres_channels_sem); return (OK); } } if ( status = clean_channels() ) { CS_ACLR(&cpres_channels_sem); return (status); } if ( cpres_num_channels_assigned < (sizeof(cpres_channels)/sizeof(CP_CHANNEL)) ) { /* ** New process, and room remains in the channel array, so assign a ** channel. */ NewCPchan = &cpres_channels[cpres_num_channels_assigned]; NMgtAt("II_INSTALLATION", &inst_id); if (inst_id && *inst_id) STprintf(mbx_name, "II_CPRES_%s_%x", inst_id, (i4)pid); else STprintf(mbx_name, "II_CPRES_%x", (i4)pid); name_desc.dsc$a_pointer = mbx_name; name_desc.dsc$w_length = STlength(mbx_name); name_desc.dsc$b_dtype = DSC$K_DTYPE_T; name_desc.dsc$b_class = DSC$K_CLASS_S; vms_status = sys$assign(&name_desc, /* devname */ &NewCPchan->chan, /* channel */ 0, /* access_mode */ 0, /* mbxnam */ AGN$M_WRITEONLY); /* flags */ if ( vms_status == SS$_NORMAL ) { NewCPchan->pid = pid; NewCPchan->state = CPchanIsAlive; *CPchan = NewCPchan; cpres_num_channels_assigned++; CS_ACLR(&cpres_channels_sem); return (OK); } else { STprintf(msg_buf, "%x cpres_mbx_assign: Error (%x) assigning channel to %s", Cs_srv_block.cs_pid, vms_status, mbx_name); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); } } else { /* ** No room left in channels array. One possibility is to go through ** the array looking for assigned channels to mailboxes of dead ** processes and clean those up. */ STcopy("PANIC! No room left in channels array!", msg_buf); ERlog(msg_buf, STlength(msg_buf), &local_sys_err); } CS_ACLR(&cpres_channels_sem); return (FAIL); }
STATUS ERsend(i4 flag, char *message, i4 msg_length, CL_ERR_DESC *err_code) { # ifdef NT_GENERIC static bool er_init = FALSE; static bool is_w95 = FALSE; # else /* !NT_GENERIC */ static int er_ifi = -2; static int ar_ifi = -2; # endif /* !NT_GENERIC */ STATUS status; char tmp_buf[ER_MAX_LEN]; char* logmsg = message; /* Check for bad paramters. */ CL_CLEAR_ERR( err_code ); if ((message == 0 || msg_length == 0) && flag != ER_AUDIT_MSG) return (ER_BADPARAM); if ((flag != ER_ERROR_MSG) && (flag != ER_AUDIT_MSG) && ( flag != ER_OPER_MSG)) return (ER_BADPARAM); # ifndef NT_GENERIC if (flag & ER_AUDIT_MSG) { key_t msg_key; char *ipc_number; struct { long mtype; char mtext[ER_MAX_LEN]; } msg; if (ar_ifi == -2) { NMgtAt("II_AUDIT_IPC", &ipc_number); if (ipc_number && ipc_number[0]) { CVal(ipc_number, &msg_key); ar_ifi = msgget(msg_key, 0); if (ar_ifi == -1) { SETCLERR(err_code, 0, ER_open); return(ER_NO_AUDIT); } } else { SETCLERR(err_code, 0, ER_open); return(ER_NO_AUDIT); } } /* Handle special case to connect only but not send message. */ if (msg_length == 0 && message == 0) return (OK); MEcopy(message, msg_length, msg.mtext); msg.mtype = 1; if (msgsnd(ar_ifi, &msg, msg_length, 0)) { SETCLERR(err_code, 0, ER_open); return(ER_BADSEND); } return (OK); } else # endif /* ! NT_GENERIC */ if (flag & ER_OPER_MSG) { char hostname[GL_MAXNAME]; STATUS status; message[msg_length] = EOS; TRdisplay("ER Operator:\"%s\"\n",message); if (!ERsysinit) ERinitsyslog(); # ifdef NT_GENERIC { wchar_t *wmessage = NULL; /* ** Update the ReportEvent to report information in the event log. */ if ( ReportEvent( EventLog, (WORD) EVENTLOG_INFORMATION_TYPE, (WORD) 0, /* event category */ (DWORD) I_ING_INFO, /* event identifier */ (PSID) NULL, (WORD) 1, /* number of strings */ (DWORD) 0, &message, NULL ) == FALSE) status = GetLastError(); if ( !er_init ) { char VersionString[256]; FUNC_EXTERN BOOL GVosvers(char *OSVersionString); GVosvers(VersionString); is_w95 = ( STstrindex(VersionString, "Microsoft Windows 9", 0, FALSE) != NULL ) ? TRUE : FALSE; if ( !is_w95 ) /* netapi32 only on NT */ { HANDLE hDll; if ((hDll = LoadLibrary(TEXT("netapi32.dll"))) != NULL) { pNetMessageNameAdd = (NET_API_STATUS (*)(LPCWSTR,LPCWSTR)) GetProcAddress(hDll, TEXT("NetMessageNameAdd")); pNetMessageNameDel = (NET_API_STATUS (*)(LPCWSTR,LPCWSTR)) GetProcAddress(hDll, TEXT("NetMessageNameDel")); pNetMessageBufferSend = (NET_API_STATUS (*)(LPCWSTR,LPCWSTR,LPCWSTR,LPBYTE,DWORD)) GetProcAddress(hDll, TEXT("NetMessageBufferSend")); } /* if any problem, pretend we don't support it */ if ( pNetMessageNameAdd == NULL || pNetMessageNameDel == NULL || pNetMessageBufferSend == NULL ) is_w95 = TRUE; } } if ( !is_w95 ) { /* ** Now, send the message to the server console, ** putting up a message box (if the messenger service ** is running. Everything must be in Unicode. */ if ( whostname[0] == 0 ) { unsigned int len = sizeof(hostname); /* ** get the hostname in Unicode format for use ** by messenger service */ GetComputerName( (char *)hostname, &len ); MultiByteToWideChar( GetACP(), 0, hostname, sizeof(hostname), whostname, sizeof(whostname) ); } /* initialize the messenger service */ status = (*pNetMessageNameAdd)( whostname, msgname ); if ( status != NERR_Success ) status = GetLastError(); /* Allocate a buffer for the Unicode */ wmessage = (wchar_t *) MEreqmem( 0, msg_length * sizeof(wchar_t), TRUE, &status ); if ( wmessage ) { /* copy the message to the Unicode buffer */ MultiByteToWideChar( GetACP(), 0, message, msg_length, wmessage, msg_length * sizeof(wchar_t) ); status = (*pNetMessageBufferSend)( whostname, msgname, NULL, (LPBYTE) wmessage, msg_length*sizeof(wchar_t) ); if ( status != NERR_Success ) status = GetLastError(); MEfree( (PTR)wmessage ); } /* re-initialize the messenger service */ status = (*pNetMessageNameDel)( whostname, msgname ); if ( status != NERR_Success ) status = GetLastError(); } } # elif defined(OS_THREADS_USED) && defined(any_aix) syslog_r( LOG_ALERT|LOG_ERR, message ); # else syslog( LOG_ALERT|LOG_ERR, message ); # endif /* NT_GENERIC */ } if (flag & ER_OPER_MSG) { i4 msglen = 0; char* host = PMhost(); MEfill( ER_MAX_LEN, 0, tmp_buf ); /* ** Format the message string for the event log. As the source is ** not known a fixed string of INGSYSLOG is used. */ TRformat( NULL, 0, tmp_buf, ER_MAX_LEN - 1, "%8.8t::[INGSYSLOG , 00000000]: %@ ", STlength(host), host ); msglen = STlength(tmp_buf); STcat( tmp_buf, message ); /* append original message */ msg_length += msglen; logmsg = tmp_buf; } status = ERlog( logmsg, msg_length, err_code ); return( status ); }