ALTER procedure BlockWatchDog as
/* net send to the blocker */
/* this s.p. will only work with current d.b. where the lock is. (because of OBJECT_NAME) */
declare @LockingMAC varchar(500), @LockedMAC Varchar(500), @LockingApp varchar(500),@LockedApp varchar(500),
@LockingHost varchar(50), @LockedHost varchar(50),
@Resource varchar(500), @dbid int, @v varchar(500), @rc int, @o int,
@BlockedSQL varchar(80),@Blockingsql varchar(80),
@Blockingsqlhandle binary(20), @Blockedsqlhandle binary(20),
@BlockingUserName varchar(50), @BlockedUserName varchar(50),
@BlockingUser varchar(50), @BlockedUser varchar(50)
declare c cursor local fast_forward for
select locking.nt_username,locked.nt_username,
locking.net_address,locked.net_address,locking.program_name,locked.program_name,
locking.hostname, locked.hostname, rsc_dbid, rsc_objid, locking.sql_handle, locked.sql_handle
from master..sysprocesses locked
,master..sysprocesses locking
, master..syslockinfo li
where locked.blocked<>0
and locking.spid=locked.blocked
and locked.spid=li.req_spid
and li.req_status in (2,3)
and locked.waittime between 10000 and 99000000
The above statement is the core query that actually finds the situations of users blocking each other.
exec @rc=sp_oacreate 'vrSQLHelper.Network', @o OUT
open c
fetch next from c into @LockingMac , @LockedMac , @LockingApp ,@LockedApp , @LockingHost, @LockedHost, @dbid,@resource, @Blockingsqlhandle, @Blockedsqlhandle
WHILE @@FETCH_STATUS = 0
begin
select @blockingsql=' blocking with: ' + left(cast(text as varchar(5000)),80) from ::fn_get_sql(@blockingsqlhandle)
select @blockedsql =' blocked code: ' + left(cast(text as varchar(5000)),80) from ::fn_get_sql(@blockedsqlhandle)
if @LockingApp not like 'SQLAgent%' and @LockedApp not like 'SQLAgent%' begin
set @Lockingapp=rtrim(isnull(@lockingapp,''))
set @Lockedapp=rtrim(isnull(@lockedapp,''))
set @lockingHost=NULL
if @lockingApp = '' set @Lockingapp = 'unidentified application'
if @lockedApp = '' set @Lockedapp = 'unidentified application'
set @LockedHost=NULL
if @rc=0 and @LockingMAC<>'' exec sp_oagetproperty @o,'HOSTfrommac',@LockingHost OUT,@LockingMAC
if @rc=0 and @LockedMAC <>'' exec sp_oagetproperty @o,'HOSTfrommac',@LockedHost OUT,@LockedMAC
Now that we know the true workstation name, we can proceed with NET SEND. However, in order to make the messages helpful, we will want to include contact information of the both parties, so they know how to contact each other, and, hopefully, do it without bothering the busy admin (possibly,
you :-). We will extract this information from the Active Directory basing on the username.
if rtrim(@BlockingUserName)<>''
select @BlockingUser=cn + ', ' + telephonenumber from
OPENROWSET('ADsDSOObject','Integrated Security=SSPI',
'SELECT cn,telephonenumber, sAMAccountName
from ''LDAP://DC=my,dc=domain,dc=com''
WHERE objectCategory = ''Person'' AND objectClass = ''user'' ')
where sAMAccountName=@BlockingUserName
if rtrim(@BlockedUserName)<>''
select @BlockedUser=cn + ', ' + telephonenumber from
OPENROWSET('ADsDSOObject', 'Integrated Security=SSPI',
'SELECT cn,telephonenumber,sAMAccountName
from ''LDAP://DC=my,dc=domain,dc=com''
WHERE objectCategory = ''Person'' AND objectClass = ''user'' ')
where sAMAccountName=@BlockedUserName
In the above,
dc=my,dc=domain,dc=com represents the DNS domain.
if @LockingHost is not null
begin
if @BlockedUser is not null
set @v = 'NET SEND ' + @LockingHost + ' Your ' + @LockingApp + ' is blocking ' + @BlockedUser + '. Please complete your operation.'
else
set @v = 'NET SEND ' + @LockingHost + ' Your ' + @LockingApp + ' is blocking ' + isnull('machine ' + upper(@lockedHost), 'another machine') + '. Please complete your operation.'
exec master..xp_cmdshell @v, no_output
end
if @LockedHost is not null
begin
if @BlockingUser is not null
set @v = 'NET SEND ' + @LockingHost + ' You are blocked by ' + @BlockingUser + '. I notified them, so if you are not unblocked soon, call them and ask to complete their transaction.'
else begin
if @lockingHost is null
set @v = 'NET SEND ' + @LockingHost + ' You are blocked by another machine that we can''t determine. Database Administrator has been notified.'
else
set @v = 'NET SEND ' + @LockedHost + ' You are blocked by machine ' + upper(@lockinghost) + '. That machine and database administrator have been notified.'
end
exec master..xp_cmdshell @v, no_output
end
if @LockingApp = 'unidentified application' set @Lockingapp = 'Unidentified application'
set @v = @LockingApp + ' on ' + upper(@lockingHost) + ' is blocking ' + @LockedApp + ' on ' + upper(@LockedHost) + ' ; resource: ' + @tname + isnull(@blockingsql,'') + isnull(@blockedsql,'')
exec master..xp_logevent 50002,@v,WARNING
Option Explicit
Private Const MAX_ADAPTER_NAME_LENGTH As Long = 256
Private Const MAX_ADAPTER_DESCRIPTION_LENGTH As Long = 128
Private Const MAX_ADAPTER_ADDRESS_LENGTH As Long = 8
Private Const ERROR_SUCCESS As Long = 0
Private Const MAXLEN_PHYSADDR = 8
Private Type MIB_IPNETROW
dwIndex As Long
dwPhysAddrLen As Long
bPhysAddr(0 To MAXLEN_PHYSADDR - 1) As Byte
dwAddr As Long
dwType As Long
End Type
Private Type IP_ADDRESS_STRING
IpAddr(0 To 15) As Byte
End Type
Private Type IP_MASK_STRING
IpMask(0 To 15) As Byte
End Type
Private Type IP_ADDR_STRING
dwNext As Long
IpAddress As IP_ADDRESS_STRING
IpMask As IP_MASK_STRING
dwContext As Long
End Type
Private Type IP_ADAPTER_INFO
dwNext As Long
ComboIndex As Long 'reserved
sAdapterName(0 To (MAX_ADAPTER_NAME_LENGTH + 3)) As Byte
sDescription(0 To (MAX_ADAPTER_DESCRIPTION_LENGTH + 3)) As Byte
dwAddressLength As Long
sMAC(0 To (MAX_ADAPTER_ADDRESS_LENGTH - 1)) As Byte
dwIndex As Long
uType As Long
uDhcpEnabled As Long
CurrentIpAddress As Long
IpAddressList As IP_ADDR_STRING
GatewayList As IP_ADDR_STRING
DhcpServer As IP_ADDR_STRING
bHaveWins As Long
PrimaryWinsServer As IP_ADDR_STRING
SecondaryWinsServer As IP_ADDR_STRING
LeaseObtained As Long
LeaseExpires As Long
End Type
Private Declare Function GetIpNetTable Lib "Iphlpapi" (pIpNetTable As Byte, pdwSize As Long, ByVal bOrder As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Function GetAdaptersInfo Lib "Iphlpapi" _
(AdapterAddresses As Any, BK As Long) As Long
Public Property Get IPfromMAC(ByVal Mac As String) As Variant
On Error GoTo ex
IPfromMAC = Null
If Len(Mac) <> 12 And Len(Mac) <> 17 Then
Exit Property
End If
Mac = UCase(Mac)
Dim Mac0 As String: Mac0 = Mac
If InStr(Mac, "-") = 0 Then
Mac = CInt("&H" & Mid(Mac, 1, 2)) & "." & CInt("&H" & Mid(Mac, 3, 2)) & "." & CInt("&H" & Mid(Mac, 5, 2)) & _
"." & CInt("&H" & Mid(Mac, 7, 2)) & "." & CInt("&H" & Mid(Mac, 9, 2)) & "." & CInt("&H" & Mid(Mac, 11, 2))
Else
Mac = CInt("&H" & Mid(Mac, 1, 2)) & "." & CInt("&H" & Mid(Mac, 4, 2)) & "." & CInt("&H" & Mid(Mac, 7, 2)) & _
"." & CInt("&H" & Mid(Mac, 10, 2)) & "." & CInt("&H" & Mid(Mac, 13, 2)) & "." & CLng("&H" & Mid(Mac, 16, 2))
End If
IPfromMAC = GetIPfromARP(UCase$(Mac))
' The previous ARP lookup does not include our own addresses. Check them now
If VarType(IPfromMAC) = vbNull Then
IPfromMAC = IPfromOurMAC(Mac0)
End If
Exit Property
ex:
End Property
Public Property Get HostfromMAC(ByVal Mac As String) As Variant
Dim vIP As Variant
Mac = UCase(Mac)
vIP = IPfromMAC(Mac)
HostfromMAC = Null
If VarType(vIP) <> vbNull Then
HostfromMAC = GetHostNameFromIP(vIP)
If VarType(HostfromMAC) <> vbNull Then
If InStr(HostfromMAC, ".") <> 0 Then HostfromMAC = Split(HostfromMAC, ".")(0)
End If
End If
End Property
Private Function GetIPfromARP(Mac) As Variant
GetIPfromARP = Null
On Error GoTo ex
'KPD-Team 2001
'URL: http://www.allapi.net/
'E-Mail: KPDTeam@Allapi.net
Dim Listing() As MIB_IPNETROW, Ret As Long, cnt As Long
Dim bBytes() As Byte, bTemp(0 To 3) As Byte
'set the graphics mode of this form to persistent
'call the`` function to retrieve how many bytes are needed
GetIpNetTable ByVal 0&, Ret, False
'if it failed, exit the sub
If Ret <= 0 Then Exit Function
'redimension our buffer
ReDim bBytes(0 To Ret - 1) As Byte
'retireve the data
GetIpNetTable bBytes(0), Ret, False
'copy the number of entries to the 'Ret' variable
CopyMemory Ret, bBytes(0), 4
'redimension the Listing
If Ret > 0 Then ReDim Listing(0 To Ret - 1) As MIB_IPNETROW
'show the data
For cnt = 0 To Ret - 1
Dim PA As String
CopyMemory Listing(cnt), bBytes(4 + 24 * cnt), 24
CopyMemory bTemp(0), Listing(cnt).dwAddr, 4
PA = UCase$(ConvertAddressToString(Listing(cnt).bPhysAddr, Listing(cnt).dwPhysAddrLen))
If PA = Mac Then
GetIPfromARP = ConvertAddressToString(bTemp(), 4)
Exit Function
End If
Next cnt
ex:
End Function
'converts a byte array to a string
Private Function ConvertAddressToString(bArray() As Byte, lLength As Long) As String
Dim cnt As Long
For cnt = 0 To lLength - 1
ConvertAddressToString = ConvertAddressToString + CStr(bArray(cnt)) + "."
Next cnt
ConvertAddressToString = Left$(ConvertAddressToString, Len(ConvertAddressToString) - 1)
End Function
Private Function IPfromOurMAC(ByVal Mac As String) As Variant
IPfromOurMAC = Null
If Len(Mac) <> 17 And Len(Mac) <> 12 Then Err.Raise 555, , "Invalid MAC supplied"
If Len(Mac) = 17 And Mid(Mac, 3, 1) = "-" Then ' remove dashes
Mac = Mid(Mac, 1, 2) & Mid(Mac, 4, 2) & Mid(Mac, 7, 2) & Mid(Mac, 10, 2) & Mid(Mac, 13, 2) & Mid(Mac, 16, 2)
End If
Dim rc As Long, rLen As Long, bArr() As Byte, Found As Boolean, Ptr1 As Long
Dim Adapter As IP_ADAPTER_INFO
rc = GetAdaptersInfo(ByVal 0&, rLen)
If rc <> 111 Then Err.Raise 555, , "Failed to get adapters info"
ReDim bArr(0 To rLen - 1)
rc = GetAdaptersInfo(bArr(0), rLen)
If rc <> 0 Then Err.Raise 555, , "Failed to get adapters info"
'get a pointer to the data stored in buff()
Ptr1 = VarPtr(bArr(0))
Do While (Ptr1 <> 0)
'copy the data from the pointer to the
'first adapter into the IP_ADAPTER_INFO type
CopyMemory Adapter, ByVal Ptr1, LenB(Adapter)
With Adapter
Dim CurrentMAC As String, i As Integer, NextDigit As String
CurrentMAC = ""
For i = 0 To 5
NextDigit = Hex(.sMAC(i))
If Len(NextDigit) = 1 Then NextDigit = "0" & NextDigit
CurrentMAC = CurrentMAC & NextDigit
Next i
If CurrentMAC = Mac Then
IPfromOurMAC = TrimNull(StrConv(.IpAddressList.IpAddress.IpAddr, vbUnicode))
Exit Function
End If
Ptr1 = .dwNext
End With 'With Adapter
'ptr1 is 0 when (no more adapters)
Loop 'Do While (ptr1 <> 0)
End Function
Private Function TrimNull(item As String)
Dim pos As Integer
pos = InStr(item, Chr$(0))
If pos Then
TrimNull = Left$(item, pos - 1)
Else: TrimNull = item
End If
End Function
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)