Wednesday, June 02, 2010

Removing Firefox (BATCH)

For whatever reason, I had/have the task of removing Mozilla Firefox from our PCs in an automated way. I figured I could do it from a batch file, a .CMD file, but here's the proof:

@ECHO OFF
CLS
ECHO Checking for Firefox...
FOR /F "tokens=* skip=4 delims=" %%a in ('reg query "HKLM\Software\Mozilla\Mozilla Firefox" /v CurrentVersion') do SET DATA=%%a
IF "%DATA:~31,1%"=="." goto :longVersion
SET CVersion=%DATA:~26,5%
SET FVersion=%DATA:~26,20%
goto :versionCaptured
:longVersion
SET CVersion=%DATA:~26,7%
SET FVersion=%DATA:~26,20%
:versionCaptured

FOR /F "tokens=* skip=4" %%a in ('reg query "HKLM\Software\Mozilla\Mozilla Firefox\%FVersion%\Main" /v "Install Directory"') do SET DATA=%%a
SET IFolder=%DATA:~25%
ECHO *%CVersion%*
ECHO *%FVersion%*
ECHO *%IFolder%*
IF "%CVersion%"=="3.5.9" goto :DoNotRemove
goto :RemoveFirefox

:RemoveFirefox
echo The installed version is not approved for use in our environment.
TASKKILL /F /IM firefox.exe
CALL "%IFolder%\uninstall\helper.exe" /S
goto :eof

:DoNotRemove
echo This version is approved for use in our environment.
goto :eof

We like version 3.5.9, so we keep that version, but we need to revert or upgrade any other version. This is part of a bigger process, but...

Yesterday... Working with Dates

Recently we ran into an issue where we were looking into why the data wasn't being returned on the first of the month for a particular web report. What we found was simple bad math, but what I learned was that sometimes efficiencies are found in what might appear more complex.

Below you will find the method used. and the problem is the subtracting of one (1) from the day of the month (DAY(GETDATE())-1). On the first of the month, this would result in a ZERO (0) which is impossible and will not return a result. The same can be said for using this sort of math on the month when your month is January. Not a good plan.
/* ---------------------------------------------
 This is the method in use...
   --------------------------------------------- 
*/
SELECT DISTINCT [DateModified]
  FROM [ITDS].[dbo].[Tickets] a
WHERE
  YEAR(a.DateModified) = YEAR(GETDATE()) AND
  MONTH(a.DateModified) = MONTH(GETDATE()) AND
  DAY(a.DateModified) = DAY(GETDATE())-1
  ORDER BY [DateModified] Desc
GO

Another method is to use DATEDIFF() as a filter on the age (1=Yesterday). This is much heavier a load on the server and you might be better-off to avoid it.

/* ---------------------------------------------
 This method is inefficient and will impact 
 performance more with large tables
   --------------------------------------------- 
*/
SELECT DISTINCT [DateModified], DATEDIFF(dd,a.DateModified,GETDATE()) as Age
  FROM [ITDS].[dbo].[Tickets] a
WHERE
  DATEDIFF(dd,a.DateModified,GETDATE())=1
  ORDER BY [DateModified] Desc
GO

Now, this combination of methods, using YEAR(), MONTH(), and DAY(), with the proper calculation of Yesterday is the charm. By setting the variable at the beginning, before the query, it is calculated once and the values for Year, Month, and Day are a simple comparison rather than the more complex effort that goes into DATEDIFF() from our prior example.

/* ---------------------------------------------
 This is the method adopted...
   --------------------------------------------- 
*/
DECLARE @Yesterday as DateTime;
SET @Yesterday=DATEADD(dd,-1,GETDATE());

SELECT DISTINCT [DateModified]
  FROM [ITDS].[dbo].[Tickets] a
WHERE
  YEAR(a.DateModified) = YEAR(@Yesterday) AND
  MONTH(a.DateModified) = MONTH(@Yesterday) AND
  DAY(a.DateModified) = DAY(@Yesterday)
  ORDER BY [DateModified] Desc
GO

Another advantage of this method is that if you want to filter on last month's data rather than Yesterday's it's a few small changes.

/* ---------------------------------------------
 This is the method adopted... Last Month
   --------------------------------------------- 
*/
DECLARE @LastMonth as DateTime;
SET @LastMonth=DATEADD(mm,-1,GETDATE());

SELECT DISTINCT [DateModified]
  FROM [ITDS].[dbo].[Tickets] a
WHERE
  YEAR(a.DateModified) = YEAR(@LastMonth) AND
  MONTH(a.DateModified) = MONTH(@LastMonth)
  ORDER BY [DateModified] Desc
GO

The best scripters/programmers learn from their own mistakes as well as those others have made.


Addendum: 
There's another way to get "yesterday" that I saw, though I still believe the preferred method might be the adopted method above, where the WHERE condition would read/include:
(a.DateModified >= DATEADD(dd,-1,GETDATE()) AND a.DateModified <= DATEADD(dd,-0,GETDATE()))
The problem I see in this methodology is that you would get the last 24-period, not "yesterday" and the wastefulness of "DATEADD(dd,-0,GETDATE())" in that it really gives you nothing more that GETDATE() anyway. The result would give you, supposing that today might be August 10th, 2010 at 11:47:01 AM, August 9th, 2010 at 11:47:01AM through August 10th, 2010 at 11:47:01 AM. In effect the results could change through the day depending upon the time you ran the query. The adopted methodology would give you an absolute definition of yesterday, or last month, eliminating the time-of-day factor, which is preferred.

Saturday, May 08, 2010

Adapting to the ever changing drive letters...

...by using unique volume labels.

Okay, this isn't a complete thought yet, just some ideas I've mulled over and found a solution for for myself, but feel free to adapt and play with it. The reason I came up with this is because I picked up a new toy, a pogoplug (v2) and it provides me with network attached storage of multiple USB Drives (External Hard Disks or USB Keys). That's great, and it also provides for the automated backup of files/folders on my PC to the device, so why would I want to control it any further?

Because I can. The drive letters can change from time to time, depending on the timing of this or that being able to adapt to the changing drive letter seemed like a good idea. So, I fired up Notepad++ and wrote myself a VBScript tool that can do a bunch of things, but in this instance it can locate the drive letter for the volume name and I can parse that using CScript (command-line VBScript, vs. WScript, which would run graphical). Now I first ran cscript //H:CScript to ensure that it runs like a "DOS App" but you could easily change the batch file portion of this to simply run this in cscript. The follwing is the VBScript:

Option Explicit
' Find My Drive (ListDrives.vbs)

DIM strVolume, strFound, objDictionary, objWMIService, colDisks, objDisk, strComputer
DIM flagHideList, strDrive, wshShell, wshSystemEnv, wshCurrentEnv, strSetVar
DIM i

flagHideList=false
strFound=""
strVolume=""
strComputer = "."

Set wshShell = CreateObject( "WScript.Shell" )
Set wshCurrentEnv = wshShell.Environment( "VOLATILE" ) ' PROCESS, SYSTEM, USER and VOLATILE
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

If Wscript.Arguments.Count = 0 Then
    ' Do Proceed as Normal
Else
    For i = 0 to Wscript.Arguments.Count - 1
  If lcase(Wscript.Arguments(i)) = "/volume" Then
            if i+1 <= Wscript.Arguments.Count then
    strVolume=Wscript.Arguments(i+1)
   end if
  end if
  If lcase(Wscript.Arguments(i)) = "/setvar" Then
            if i+1 <= Wscript.Arguments.Count then
    strSetVar=Wscript.Arguments(i+1)
   end if
  end if
  If lcase(Wscript.Arguments(i)) = "/hidelist" Then
   flagHideList=true
  end if
 next
End If

Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")

For Each objDisk in colDisks
    objDictionary.Add objDisk.DeviceID, objDisk.VolumeName
 if NOT flagHideList then WScript.Echo objDisk.DeviceID & " " & objDisk.VolumeName
Next

For i = 67 to 90
    strDrive = Chr(i) & ":"
    If objDictionary.Exists(strDrive) Then
  if strVolume=ObjDictionary.Item(strDrive) then strFound=Chr(i) & ":"
    End If
Next
if strFound<>"" then
 WScript.Echo strVolume & "=" & strFound
 if strSetVar<>"" then
  ' Display the current value
  WScript.Echo strSetVar & "=" & wshCurrentEnv( strSetVar )
  ' Set the environment variable
  wshCurrentEnv( strSetVar ) = strFound
  WScript.Echo strSetVar & "=" & wshCurrentEnv( strSetVar )
 end if
end if
WScript.Quit 0
...and yes, this could be enhanced and/or streamlined.

So. To find a Volume name "Tom's Drive" you would type:
ListDrives.vbs /volume "Tom's Drive"

Now that's cute, but useless beyond yourself... what you can do is automate it... throw this into a batch file:

FOR /F "usebackq tokens=1,2 skip=3 delims==" %%i IN (`ListDrives.vbs /volume "Tom's Drive" /hidelist`) DO SET DL=%%j 

or

FOR /F "usebackq tokens=1,2 skip=3 delims==" %%i IN (`cscript ListDrives.vbs /volume "Tom's Drive" /hidelist`) DO SET DL=%%j 

You can now use the environment variable %DL% as the drive letter. You can use this dealing with All sorts of USB Keys, or even a memory card. There are other tricks too, but this was just an idea.

Tuesday, April 06, 2010

Using T-SQL to Decode a Decimal (BIGINT) IP Address

A site I ran across stored an IP Address as a BIGINT decimal number, to reveal the more friendly IP Address use either of the following methods to extract the octets or the Friendly IP. These formulae will allow you to return the IP Address in a usable manner.

IPAddress IPOCTET1 IPOCTET2 IPOCTET3 IPOCTET4 IPAddressEX
2886730061 172 16 1 77 172.16.1.77
2886730059 172 16 1 75 172.16.1.75
2886730053 172 16 1 69 172.16.1.69
2886730046 172 16 1 62 172.16.1.62
2886730047 172 16 1 63 172.16.1.63
2886730303 172 16 2 63 172.16.2.63
,[IPAddress]
You can use:
, CAST( CAST([IPAddress]/POWER(256,3) AS BIGINT) AS VARCHAR(3) ) AS IPOCTET1 , CAST( CAST( CAST( [IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT) AS VARCHAR(3)) AS IPOCTET2 , CAST( CAST( CAST([IPAddress] -CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) -CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2) AS INTEGER)/256 AS INTEGER) AS VARCHAR(3)) AS IPOCTET3 , CAST( [IPAddress]- (CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3)+ CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2)+ CAST( CAST( [IPAddress] -CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) -CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2) AS BIGINT)/256 AS BIGINT)*256) AS VARCHAR(3)) AS IPOCTET4
or use this to build a properly concatenated string.
, CAST( CAST([IPAddress]/POWER(256,3) AS BIGINT) AS VARCHAR(3) ) + '.' + CAST( CAST( CAST( [IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT) AS VARCHAR(3)) + '.' + CAST( CAST( CAST([IPAddress] -CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) -CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2) AS INTEGER)/256 AS INTEGER) AS VARCHAR(3)) + '.' + CAST( [IPAddress]- (CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3)+ CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2)+ CAST( CAST( [IPAddress] -CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) -CAST(CAST([IPAddress]-CAST([IPAddress]/POWER(256,3) AS BIGINT)*POWER(256,3) AS BIGINT)/POWER(256,2) AS BIGINT)*POWER(256,2) AS BIGINT)/256 AS BIGINT)*256) AS VARCHAR(3)) AS IPAddressEX


...and Back

To find a printer, in this example by searching for one in SQL, that IP Address might seem fairly useless looking at the numbers involved. You could calculate it yourself by taking the Octets of the IP Address and using this formula:
16777216*Octet1 + 65536*Octet2 + 256*Octet3 + Octet4
...but we like automation.

DECLARE @IPAddr varchar(16); DECLARE @IPAddrAsDecimal BIGINT; DECLARE @IPAddrOctet1 BIGINT; DECLARE @IPAddrOctet2 BIGINT; DECLARE @IPAddrOctet3 BIGINT; DECLARE @IPAddrOctet4 BIGINT; DECLARE @intStartPos INT; DECLARE @intEndPoint INT SET @IPAddr='10.176.24.138'; --SET @IPAddr='172.16.2.66'; SELECT @IPAddrOctet1=CONVERT(BIGINT, LEFT(@IPAddr,CHARINDEX('.', @IPAddr)-1)); SELECT @intStartPos=(LEN(@IPAddrOctet1)+2); SELECT @intEndPoint=CHARINDEX('.', @IPAddr, @intStartPos); SELECT @IPAddrOctet2=CONVERT(BIGINT, SUBSTRING(@IPAddr,@intStartPos,@intEndPoint-@intStartPos)); SELECT @intStartPos=(LEN(@IPAddrOctet1)+LEN(@IPAddrOctet2)+3); SELECT @intEndPoint=CHARINDEX('.', @IPAddr, @intStartPos); SELECT @IPAddrOctet3=CONVERT(BIGINT, SUBSTRING(@IPAddr,@intStartPos,@intEndPoint-@intStartPos)); SELECT @intStartPos=(LEN(@IPAddrOctet1)+LEN(@IPAddrOctet2)+LEN(@IPAddrOctet3)+4); SELECT @intEndPoint=LEN(@IPAddr); SELECT @IPAddrOctet4=CONVERT(BIGINT, RIGHT(@IPAddr,@intEndPoint-@intStartPos+1)); SELECT @IPAddrOctet1 as Octet1, @IPAddrOctet2 as Octet2, @IPAddrOctet3 as Octet3, @IPAddrOctet4 as Octet4 SELECT @IPAddrAsDecimal=16777216*@IPAddrOctet1 + 65536*@IPAddrOctet2 + 256*@IPAddrOctet3 + @IPAddrOctet4; SELECT @IPAddrAsDecimal as IPAddressAsDecimal; --/* SELECT [IPAddress] ,[PrinterName] ,[PModelID] ,[SiteID] ,[MAC] ,[Location] ,[Approved] ,[Hits] ,[Agent] ,[TouchDate] FROM [oad].[dbo].[PrinterList] WHERE IPAddress = @IPAddrAsDecimal ; --*/

There is no individual ownership when you are part of a team, it's the sum of the parts that makes you the RESILIENT team you need to be.