See Sharp Objects, Run with Sharp Objects, Crash with Sharp Objects – Obscure and Incorrect Error Messages in Commercial Products

15 01 2016

January 15, 2016

Since October 2015 I have been fighting with an application programming interface (API) that works with an enterprise resource planning (ERP) system.  The API, with uses Microsoft .Net 4.0 or higher, is intended to simplify the process of automating the creation of various types of documents within the ERP system.  For example, the API could be used to quickly create 1,000 new part numbers in the ERP system, using an Excel spreadsheet as the source data (a custom program would need to read the Excel spreadsheet and plug that information into the formatting syntax expected by the API).  The API could also be used for creating work orders in the ERP system which would then be used to produce the parts, receive purchased parts into inventory, issue parts in inventory to work orders, receive completed parts into inventory, create shipping documents to send the parts to customers, generate invoice records to accept payment from customers, and a variety of other types of documents.  I have been using Microsoft C# 2015 to interface with the API, and the ERP company helpfully includes a standalone executable (.EXE) that is able to test the API to make certain that all is working well with the API communication to the Oracle database through the Oracle Managed Data Provider through .Net.  This API replaces an older API that exposed a COM interface, which I previously used extensively to automate the creation of various types of documents.

Why have I been fighting with the application programming interface for this ERP system?  To err is human, but two errors does not compute.  While trying to determine how to use the API, I copied a code sample from the API documentation into a new project in Microsoft C# 2015.  The documentation indicates that the methods for using the API are similar to those that I used in the older API that exposed a COM interface, so it appeared that the process would be relatively easy transition to the new API.  That was my thought, any way, until I attempted to run the program.

SeeSharp2FailedEnableConstraintsC

That cannot be good.  I suppose that it is possible that the database schema has non-enabled constraints, as it had just gone through a conversion from an earlier version of the ERP system to the latest version a couple of days prior.  Let’s check:

SELECT
  CONSTRAINT_NAME,
  CONSTRAINT_TYPE,
  TABLE_NAME,
  STATUS
FROM
  DBA_CONSTRAINTS
WHERE
  OWNER='SYSADM'
  AND STATUS<>'ENABLED';
 
no rows selected

I guess that is not the cause of the error.  Let’s take the code out of a try block, and run the program again (click the picture for a larger view):

SeeSharp2FailedEnableConstraintsCExcept

A System.Data.ConstraintException in LsaCore.dll, Failed to enable constraints.  One or more rows contain values violating non-null, unique, or foreign-key constraints error.  Ouch, what did I do wrong?  I was just trying to create a new part number in the database, and the exception was thrown before any part specifications were provided.  Maybe it is because I am trying to develop on a 64 bit computer, or maybe there is some missing Windows registry entry that I still need to create?

Let’s try the standalone executable (.EXE) that the ERP provider distributes to test the API – on a 32 bit computer that was used for a test deployment of the new version of the ERP system.  This computer was an otherwise fresh install of Windows 7 with all security updates installed prior to the deployment of the new version of the ERP system (click the picture for a larger view):

SeeSharp2FailedEnableConstraintsAPITest

That “Failed to enable constraints.  One or more rows contain values violating non-null, unique, or foreign-key constraints.” error message is a bit too familiar at this point.  I need a 10046 trace at level 12 to see what might be triggering the constraints error.  Logging into the database as the SYS user using SQL*Plus, I created an after logon trigger to enable a 10046 trace for my C# test program as well as the API test tool provided with the ERP system:

CREATE OR REPLACE TRIGGER LOGON_CAPTURE_10046 AFTER LOGON ON DATABASE
DECLARE
  SHOULD_EXECUTE INTEGER;
  TRACEFILE VARCHAR2(150);
BEGIN
  SELECT DECODE(SUBSTR(UPPER(PROGRAM),1,5),'VISUA',1,0)+DECODE(SUBSTR(UPPER(PROGRAM),1,5),'VMFGC',1,0) INTO SHOULD_EXECUTE FROM V$SESSION WHERE SID=(SELECT SID FROM V$MYSTAT WHERE ROWNUM=1);
  IF SHOULD_EXECUTE > 0 THEN
    TRACEFILE := 'ALTER SESSION SET TRACEFILE_IDENTIFIER = '''||USER||TO_CHAR(SYSDATE,'YYMMDDHH24MI')||'''';
    EXECUTE IMMEDIATE TRACEFILE;
    EXECUTE IMMEDIATE 'ALTER SESSION SET MAX_DUMP_FILE_SIZE=UNLIMITED';
    EXECUTE IMMEDIATE 'ALTER SESSION SET TIMED_STATISTICS=TRUE';
    EXECUTE IMMEDIATE 'ALTER SESSION SET EVENTS ''10046 TRACE NAME CONTEXT FOREVER, LEVEL 12''';
  END IF;
END;
/

Repeating the test with my C# test program, I found that a few 10046 trace files were created on the database server each time I ran the C# test program.  One of the 10046 trace files included:

PARSE ERROR #425887032:len=42 dep=0 uid=159 oct=3 lid=159 tim=2402951856734 err=904
SELECT DB_VERSION FROM APPLICATION_GLOBAL
WAIT #425887032: nam='SQL*Net break/reset to client' ela= 2 driver id=1413697536 break?=1 p3=0 obj#=-1 tim=2402951856800
WAIT #425887032: nam='SQL*Net break/reset to client' ela= 559 driver id=1413697536 break?=0 p3=0 obj#=-1 tim=2402951857369
WAIT #425887032: nam='SQL*Net message to client' ela= 1 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2402951857387

That is interesting.  There is no column named DB_VERSION in the APPLICATION_GLOBAL table, but there is a column named DBVERSION.  Maybe that is the cause of the Failed to enable constraints error?  After double-checking the table definition, the column is in fact supposed to be named DBVERSION, not DB_VERSION, so maybe the programmer that created the API DLLs made a typo?

Taking a look at another trace file, I found another potential problem:

PARSE ERROR #415492776:len=45 dep=0 uid=159 oct=3 lid=159 tim=2402951781868 err=942
SELECT DB_VERSION FROM VQ_APPLICATION_GLOBAL
WAIT #415492776: nam='SQL*Net break/reset to client' ela= 2 driver id=1413697536 break?=1 p3=0 obj#=-1 tim=2402951781960
WAIT #415492776: nam='SQL*Net break/reset to client' ela= 4892 driver id=1413697536 break?=0 p3=0 obj#=-1 tim=2402951786862
WAIT #415492776: nam='SQL*Net message to client' ela= 0 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2402951786911

There is no table in the database named VQ_APPLICATION_GLOBAL, and per the table creation script for the ERP system, that table probably should not exist.  Maybe the programmer that created the API DLLs made another typo?  Creating a virtual DB_VERSION column to see if there is any improvement:

ALTER TABLE APPLICATION_GLOBAL ADD (DB_VERSION NVARCHAR2(10) GENERATED ALWAYS AS (TRIM(DBVERSION)));

Well, that took care of the Failed to enable constraints error, but now I have a new problem when I run the C# test program:

SeeSharp2DataspaceNameVMFGC

What does “Dataspace name VMFG not found” mean?  Is that an improvement over the Failed to enable constraints error?   Let’s give the standalone executable (.EXE) that the ERP provider distributes to test the API a go on the 32 bit computer (click the picture for a larger view):

SeeSharp2DataspaceNameVMFGAPITest

“Dataspace name VMFG not found” – at least there is some level of consistency.

Switching back to the C# test program, I decided to enable a bit more of the options in the Exception Settings window (click the picture for a larger view):

SeeSharp2DataspaceNameVMFGCExcDetail

So, the “Dataspace name VMFG not found” error was thrown in the LsaDataLogic.TableDefinition.LoadTableDefinitions procedure in LsaCore.  That certainly is specific.  But, where is “VMFG” coming from, that is not the name of the database that I am trying to work with, although that is the name of a database that is frequently configured for this ERP system.  For fun I opened one of the API DLL files using Wordpad (click the picture for a larger view):

SeeSharpObjects2VMFGInDLLSource

I see VMFG. prefixing what appears to be every table that is in a SELECT type SQL statement within the DLL.  After checking with one of the most experienced ERP support people at the ERP company, the “Dataspace name VMFG not found” error was a complete mystery.  No one in support had ever seen that particular error message.  The “Failed to enable constraints.  One or more rows contain values violating non-null, unique, or foreign-key constraints.” error that I saw earlier was also a complete mystery at that line in the C# test code.

Taking another look at the multiple 10046 trace files that were created before I tried creating the virtual DB_VERSION column, I found that the API was actually sending at least four invalid SQL statements to the database:

SELECT DB_VERSION FROM APPLICATION_GLOBAL
 
SELECT DB_VERSION FROM VQ_APPLICATION_GLOBAL
 
SELECT DBVERSION , SITE_ID , COMPANY_MANAGER FROM APPLICATION_GLOBAL
 
SELECT APP_VERSION , PAY_HELP , HR_HELP FROM APPLICATION

The API was also using two different techniques to identify the name of the primary key column for a table – that table does not, and should not have a primary key per the table creation script for the ERP system:

SELECT COLS.COLUMN_NAME AS COLNAME , COLS.POSITION AS PKCOLSEQNUM FROM ALL_CONSTRAINTS CONS , ALL_CONS_COLUMNS COLS WHERE COLS.TABLE_NAME = 'APPLICATION_GLOBAL' AND CONS.CONSTRAINT_TYPE = 'P' AND CONS.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME AND CONS.OWNER = COLS.OWNER ORDER BY COLS.TABLE_NAME , COLS.POSITION
 
SELECT I.INDEX_NAME AS INDEX_NAME FROM USER_INDEXES I WHERE I.TABLE_NAME = 'APPLICATION_GLOBAL' AND I.TABLE_OWNER = 'SYSADM' AND I.UNIQUENESS = 'UNIQUE' ORDER BY I.INDEX_NAME

Wow!  I asked the experienced ERP support person if any of the above makes sense.  I am not sure if I ever received a response to that question.

So, I enabled tracing for the Oracle Managed Data Provider for .Net driver used by the API with a modification to the MACHINE.CONFIG file found in the folder C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config.  Just before the end of the configSections section of the file (about ½ way through the file – just before the line </configSections>) I added the following line:

    <section name="oracle.manageddataaccess.client" type="OracleInternal.Common.ODPMSectionHandler, Oracle.ManagedDataAccess" />

Next, just before the </configuration> line at the end of the file I added the following lines (specify the correct location of the tnsname.ora file on the tns_admin line):

  <oracle.manageddataaccess.client>
    <version number="*">
       <settings>
          <setting name="tns_admin" value="C:\Oracle\product\11.2.0\client_1\network\admin\" />
          <setting name="TraceOption" value="1" />
          <setting name="TraceLevel" value="127" />
          <setting name="TraceFileLocation" value="C:\Trace\" />
       </settings>
    </version>
  </oracle.manageddataaccess.client>

I then created a folder named Trace in the root of the C: drive (note that if UAC (User Access Control) is enabled, it may be necessary to grant everyone full permissions to that folder if it is located in the root of the C: drive).  The generated trace files initially did not seem to provide any additional insight into the problem beyond what was found in the 10046 trace files.

Earlier this week the experienced ERP support person arranged for an cross-continent video conference call with several other people at the ERP company to help identify the source of the problems that I experienced with the API for the ERP system.  That video conference call lasted a bit over two and a half hours, with no solution, but the conference call included a demonstration that the API for the ERP system does work with the sample database that is used by the ERP company.  The support person even went so far as to record and send to me a Process Monitor trace of the successful test execution, and he also enabled a 10046 trace of the successful test execution.  We only spent a couple of minutes scanning through the 10046 trace files during the call.  Oddly enough, during the successful test execution, I noticed that the following two SQL statements appeared in his 10046 trace files with parse errors:

SELECT DB_VERSION FROM APPLICATION_GLOBAL
  
SELECT DB_VERSION FROM VQ_APPLICATION_GLOBAL

Interesting, and when I pointed out those two parse errors (again, along with the other two that I mentioned earlier) to the people on the video conference call, no one knew why those SQL statements were attempted to be executed.  Along with the Process Monitor trace, the senior support person sent a copy of their sample database to me, exported using Datapump, so that I could try to understand just what was corrupt on the essentially barebones Windows 7 32 bit computer.

After finding that the ERP company’s sample database worked fine with my essentially barebones Windows 7 32 bit computer, I made a somewhat important conclusion.  The “Dataspace name VMFG not found” error is bad news, essentially it means that the API does not think that it is accessing a Visual Manufacturing database.  The DB_VERSION queries against the APPLICATION_GLOBAL table and VQ_APPLICATION_GLOBAL are apparently supposed to fail!  But why could someone on that 2.5+ hour cross-continent video conference call not come to the same conclusion?  I know that two of the people on the conference call have been heavily involved with the ERP system development for well over 15 years, so the people are undoubtedly very knowledgeable about the subject.

After dropping the virtual column that I created, I then repeated the C# test program’s tests with the two databases.  Comparing the trace files for my company’s database and the ERP company’s sample database, I found a difference (see the picture below – click for a larger view).

SeeSharp2TraceCompare

Ah, I found a difference.  With the sample database, after the API’s DLL performed an OracleDataReader.Read() call, it executed an OracleDataReader.NextResult() call.  With my company’s database, after the OracleDataReader.Read() call, the API’s DLL performed an OracleDataReader.Fill() call followed by an OracleDataReader.Close() call.  Buy why?  Here is the SQL statement that appeared in the trace file just prior to the highlighted line:

SELECT ACTIVATION_KEY , ACTIVITY_UDF_LABELS , ALLOW_EMAIL_DOC , AUDIT_REPORT_TIMES , BARCODE_MULTI_JOB ,
 COMPANY_ADDR_1 , COMPANY_ADDR_2 , COMPANY_ADDR_3 , COMPANY_CITY , COMPANY_COUNTRY , COMPANY_NAME ,
 COMPANY_STATE , COMPANY_ZIPCODE , CONFIGURATION_KEY , CUST_PRICE_EFF_REQ , CUST_UDF_LABELS , DBVERSION ,
 DEF_OLDEST_INV_TYPE , DOC_UDF_LABELS , DOCUMENT_DIRECTORY , DRAWING_FILE_PATH , DRAWING_LOCATOR ,
 DRAWING_VIEWER , EURO_CURRENCY_ID , FILTER_AVAIL_TRACE , ID , INSTALL_DATE , LAST_CONVERT_DATE ,
 MAXIMUM_USERS , MFG_INTERFACE_USED , PART_UDF_LABELS , PLM_ENABLED , PLM_URL , PROGRAM_PATCH_LVL ,
 PROJ_UDF_LABELS , PURC_QUOTE_TYPE , SD_SUBGROUP_MIN , SERIAL_NO , SESSION_TIMELIMIT , SHOP_UDF_LABELS ,
 SQLBASE_DATABASE_VERSION , SSO_ENABLED , STORE_MACROS_IN_DB , TABLE_PATCH_LVL , TRIGGER_PATCH_LVL ,
 VALIDATE_LOOKUPS , VEND_UDF_LABELS , VISUAL_USER_GROUP , VQ_DBNAME , VQ_DIRECTORY , VQ_ENABLED ,
 VQ_QUERY_USE , VR_UPDATES , WFL_CMNT_PWD_REQ , WO_PRIORITY_PATH , XBAR_SUBGROUP_MIN 
FROM APPLICATION_GLOBAL APPLICATION_GLOBAL 
ORDER BY ID

The Oracle Managed Data Provider’s OracleCommand object for that SQL statement, when I executed it in another C# test program, indicated that the expected row length is 10,238 bytes.  A problem? After trying a couple of changes in my database’s APPLICATION_GLOBAL table, I noticed that the PART_UDF_LABELS, VEND_UDF_LABELS, and CUST_UDF_LABELS columns in the ERP company’s sample database were all set to NULL.  Below is the contents of the PART_UDF_LABELS column in my company’s APPLICATION_GLOBAL table:

 "% of Plate","Heat Code/Material","Customer ID","Gross Weight","Vendor","Price Each","Surcharge","Freight","EAU (Cpy to Planning Tab)","FAB/COMB"

What, a difference between a sample database and a production database (one with 20+ years of transaction history)?  🙂

I set the PART_UDF_LABELS, VEND_UDF_LABELS, and CUST_UDF_LABELS columns to all be NULL, as they are in the sample database.  All tests in API test program were successful with my company’s database with all three of those columns set to NULL.  For sake of completeness, I then copied only the original PART_UDF_LABELS column value from my company’s database to the sample database, and managed to cause the API test program to fail with the sample database.  Interesting…

I reported my findings to the senior support person at the ERP company.  While waiting for a follow up from him (he needs to be able to reproduce my results), I performed some additional testing.  The PART_UDF_LABELS, VEND_UDF_LABELS, and CUST_UDF_LABELS columns, as well as several other columns in that table, are defined as NVARCHAR2(250) data types, allowing up to 250 characters to be stored per row.  The PLM_URL column in that table is defined as NVARCHAR2(2000), allowing up to 2,000 characters to be stored per row.  I found that by putting the following value into the PART_UDF_LABELS column, the ERP company’s API test program would complete successfully:

"% of Plate","Heat Code/Material","Customer ID","Gross Weight"

Adding back a few more characters to that column value caused the ERP company’s API test program to fail the tests:

"% of Plate","Heat Code/Material","Customer ID","Gross Weight","Vendor"

I found that if the PART_UDF_LABELS, VEND_UDF_LABELS, and CUST_UDF_LABELS columns contained up to 64 characters, the ERP company’s API test program completed the tests successfully.  65 characters caused the API test program to fail the tests.  But, what about that column (PLM_URL) that permits up to 2,000 characters to be stored, surely that column cannot have a 64 character limit for companies that need to automate document creation through the ERP company’s API?  Yep, more than 64 characters in that column will also cause the API tests to fail.

To err is human, but two errors does not compute (or was it three errors identified in this article).





See Sharp Objects, Run with Sharp Objects, Crash with Sharp Objects – Nothing but .Net, C#, and ODP.NET – 1

5 12 2015

December 5, 2015

I have been writing computer programs for a few years – the first more than 30 years ago, and have written programs in a variety of languages including C++, Cobol, Pascal, BASIC, Visual Basic, eVB, a couple of scripting languages (including JavaScript and VBScript), and a few others that are long forgotten (I could read some Fortran, but never had a need for the language).  I used Visual Basic for DOS years ago, and after toying with Visual Basic 2.0 for an hour, I wondered about the purpose of that “toy”, but interesting, programming language that allowed me to draw text boxes and drop-down lists on a form.  I bought and read cover to cover a number of books that described methods to utilize the quick development capabilities of Visual Basic to draw a user interface, and then jump out of the limited safety net of Visual Basic to harness the functionality provided by direct access to the Windows API.  The classic Visual Basic 6.0 is my favorite language so far, allowing for quick development, fast performance, and its ability to produce programs that are compatible with every Windows version from Windows NT 4.0 (and Windows 95) to Windows 10.

In my opinion, Microsoft mutated the clean, quick development programming language in Visual Basic 6.0 with the release of VB.Net.  A decade ago I had to write a program that would run on a Symbol MC3090 handheld computer with built-in barcode scanner (Symbol was later acquired by Motorola, part of Motorola’s operations were acquired by Google, and apparently Zebra is the current owner of this technology).  The MC3090 supported programs that were written in the .Net languages, such as VB.Net and C#, but not the eVB language (language was similar to VB 6.0) that was supported on the earlier Symbol PDT8146.  After a bit of head scratching, I wrote the program in VB.Net 2005.  Not only did the VB.Net solution work, it was also significantly more stable than the eVB developed solution on the PDT8146 (the hardware change may be the primary reason for the change in stability).  I still found VB.Net’s language to be cumbersome to use compared to Visual Basic 6.0 (which is very similar to the macro language in Microsoft Excel, Access, PowerPoint, Word, and Outlook) and VBScript (which is the macro language used by the ERP system where I work).

The earlier versions of the ERP system used where I work offered a COM interface to an API (application programming interface) that could be used for a variety of activities, such as creating inventory transactions, creating purchase orders, modifying customer orders, creating work orders to produce the parts ordered by the customers, and creating the shipping records when the completed parts ship to the customers.  I relied heavily on this COM interface API with Visual Basic 6.0 (or Excel, or the macro language in the ERP system) to automate many processes that previously required manual data entry into the ERP system.  Fast forward a decade, or so, and the most recent versions of the ERP system offers only a .Net compatible API to replace the COM API.  Not only is it a .Net compatible API, but it is also not compatible with Visual Studio 2005 (I thought that .Net was supposed to mark the end of DLL Hell?).  Great, another opportunity to be forced into not using VB 6.0 for development.

I am in the process of translating a MRP (material requirement planning) program that I created 10 to 12 years ago in VB 6.0 into C# to permit the use of the new .Net API.  The program has some fairly complex programming logic that uses recursive programming to drill into the multi-level BOMs (bill of materials) – in some simple cases the multi-level BOM is associated with a single work order, while in more complex cases the multi-level BOM is associated with child, grandchild, great-grandchild, and great-great-grandchild work orders.  The goal of the MRP program is to take the open customer orders in the ERP system, and to plan the top-level, child, grandchild, great-grandchild, and great-great-grandchild work orders and the associated purchased parts so that the purchased parts are available when needed by the work orders, the parts produced by the great-great-grandchild work orders are available when required by the great-grandchild work orders, the parts produced by the great-grandchild work orders are available when needed by the grandchild work orders, the parts produced by the grandchild work orders are available when required by the child work orders, the parts produced by the child work orders are available when required by the top-level work orders, and the parts produced by the top-level work orders are available on the dates specified in the customer order.  The ideal batch (lot) quantities, of course, could be different at each of the work order levels, and the quantity due to the customer on a given date will also typically fluctuate from one day to the next.

A simple illustration of the intended end-goal of the MRP program is shown in the picture below, which is the screen capture of another program that I wrote probably 15 years ago.  The customer orders (the demand) are shown on the left half of the screen capture, and the top-level work orders (the supply) are shown on the right half of the screen capture.  The quantity ordered by the customer for shipment on a given day is variable, but the work order batch (lot) sizes are standardized by part ID.  The intended goal is to permit the completion of the production of the ordered parts one weekday (Monday through Friday) earlier than the customer’s requested ship date (so that the part from a top-level work order could complete at 11:50 PM and then ship out to the customer at 5 AM the following morning).

seesharpobjectsoverview2

After using C# for a little while, for some reason the language reminds me of an odd, but very usable cross-pollination of Visual Basic 2.0 and Borland C++ that might have happened a bit over two decades ago.  Running with sharp objects is not so bad, although there are still exceptions.

After that long introduction, I will show a quick demonstration of accessing Oracle database using Visual Basic 6.0 and C# (I will use the 2005 version of C# – the newer versions provide some alternate syntax that saves a few keystrokes while programming).  The VB 6.0 sample program uses Microsoft ADO (ActiveX Data Objects) and the Oracle Provider for OLE DB (an optional feature installed with the Oracle client, see this article).  The C# sample program uses ODP.Net (Oracle Data Provider for .NET, an optional feature installed with the Oracle client, may also be installed later from a download).

Some general guidelines for programs that interact with Oracle databases:

  • Open a database connection, and keep it open for the duration of the program, if possible.
  • Use bind variables when possible, rather than specifying literals (constants) in the SQL statement that change from one execution to the next (there are a few exceptions to this rule).  Doing so helps to minimize the performance penalty of excessive hard parses.
  • Use a consistent format for all SQL statements submitted to the database – doing so maximizes the chances that a SQL statement will not need to be hard parsed when a similar SQL statement (possibly with just one extra space in the SQL statement) is already in Oracle’s library cache.
  • Perform tasks once, if possible, rather than over and over again.
  • Minimize the number of round trips to the database.  If an operation may be executed within a loop in your program, sending requests to the database for each iteration through the loop, think outside the loop to try to construct a single SQL statement that accomplishes the same task.  For instance, retrieve the required information into program variables either before or after the loop, or perform the processing within the database instance.

For this article, we will create a couple of tables and a sequence in the database for use in the program by using SQL*Plus:

CREATE TABLE PO(
  ORDER_ID VARCHAR2(15),
  VENDOR_ID VARCHAR2(15),
  ORDER_DATE DATE,
  SHIP_TO_ID NUMBER(4),
  PRIMARY KEY (ORDER_ID));
 
CREATE TABLE PO_LINE(
  ORDER_ID VARCHAR2(15),
  LINE_NO NUMBER(4),
  PART_ID VARCHAR2(30),
  QTY NUMBER(14,4),
  PRICE NUMBER(14,4),
  DUE_DATE DATE,
  PRIMARY KEY (ORDER_ID, LINE_NO));
 
CREATE TABLE PO_TRANS(
  TRANSACTION_ID NUMBER(14),
  VENDOR_ID VARCHAR2(15),
  PART_ID VARCHAR2(30),
  QTY NUMBER(14,4),
  DUE_DATE DATE,
  UNKNOWN NUMBER(14,4),
  PRIMARY KEY (TRANSACTION_ID));
 
CREATE SEQUENCE PO_TRANS_ID START WITH 1000;

The first step for VB 6.0 is to add a reference to one of the Microsoft ActiveX Data Objects Library versions.  To access this window, select References… from the Project menu:

SeeSharpObjectsDataAccessReferenceVB6-2

The first step for C# is to add a reference to Oracle.DataAccess.  To access this window, select Add Reference… from the Project menu:

SeeSharpObjectsDataAccessReferenceCS-2

C# also requires a couple of using entries to be added to the top of the program form and class code sections.  For example (the last two lines):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
using Oracle.DataAccess.Types; // ODP.NET Oracle managed provider

 

Since I only want to create a single connection to the database, and maintain that connection for potential use within a couple of program forms or code modules, in VB 6.0 I will create a module with a Global variable to maintain the database connection (note that I did not include the New keyword before ADODB, while I have not personally performed a comparison of the performance, a couple of books indicate that including the New keyword incurs a performance penalty with every access of that variable).

Global dbConnection As ADODB.Connection

In C# I would also want to have the ability to maintain that database connection for potential use within a couple of program forms or class modules, so in C# we will create a new class that I will name Global.  Inside the class module we will modify the class definition so that it is public, and create a public static variable named dbConnection that is of the type OracleConnection – the public static variable will be accessible from other code sections in the program without having to create an instance of the Global class.  The Global class should appear similar to what is seen below:

using System;
using System.Collections.Generic;
using System.Text;
using Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
using Oracle.DataAccess.Types; // ODP.NET Oracle managed provider
 
namespace SeeSharpObjectsSample
{
    public class Global
    {
        public static OracleConnection dbConnection;
    }
}

In VB 6.0 we will add some code to the Form_Load event procedure to connect to the database.  In VB 6.0, double-click the form’s background to open the code module for the Form_Load event procedure.  In the procedure we will declare a strSQL variable that will be used later in the Form_Load procedure and a strConnection variable.  On Error Resume Next will permit the program to recover gracefully if a command, such as the connection attempt, fails.  The code will connect to the or1212 database (most likely using the tnsnames.ora file to locate the database), connecting as the testuser user which also has a password of testuser, instruct the computer to set the array fetch size to 100 (to retrieve up to 100 rows with a single database fetch call), and to allow up to 40 seconds for the connection to complete.  If the connection fails, an error message is displayed on screen, and the program ends:

Private Sub Form_Load()
    Dim strSQL As String
    Dim strConnection As String
    
    On Error Resume Next
    
    strConnection = "Provider=OraOLEDB.Oracle;Data Source=or1212;User ID=testuser;Password=testuser;FetchSize=100;"
    
    Set dbConnection = New ADODB.Connection
    dbConnection.ConnectionTimeout = 40
    dbConnection.CursorLocation = adUseClient
    dbConnection.ConnectionString = strConnection
    
    dbConnection.Open
    
    If (dbConnection.State <> 1) Then
        MsgBox "Not connected to the database, program ending." & vbCrLf & Error
        End
    Else
        MsgBox "Connected"
    End If
End Sub

In C# we will add some code to the _Load event procedure to connect to the database.  In C#, double-click the form’s background to open the code module for the _Load event procedure.  In the procedure we will declare a strSQL variable that will be used later in the _Load procedure and a strConnection variable.  The try { } and catch { } blocks permit the program to recover gracefully if a command, such as the connection attempt, fails.  The code will connect to the or1212 database (most likely using the tnsnames.ora file to locate the database), connecting as the testuser user which also has a password of testuser, and to allow up to 40 seconds for the connection to complete.  In C# the array fetch size cannot be defaulted to 100, instead the array fetch size must be specified in the parameters of the individual OracleDataReader objects.  If the connection fails, an error message is displayed on screen, and the program ends:

private void frmSeeSharpObjects_Load(object sender, EventArgs e)
{
    string strSQL = "";
    string strConnection = "Data Source=or1212;User ID=testuser;Password=testuser;Connection Timeout=40;";
    
    try
    {
        Global.dbConnection = new OracleConnection(strConnection);
        
        Global.dbConnection.Open();
        MessageBox.Show("Connected");
    }
    catch (OracleException ex)
    {
        MessageBox.Show ("Not connected to the database, program ending." + "\n" + ex.Message.ToString() );
        Application.Exit();
    }
}

At this point, both of the programs should be able to run and connect to the database (Connected should appear in a window on the screen).  I happen to be using the 64 bit version of Windows for development, and need to use the 32 bit Oracle Client (and ODP.NET), so I encountered a few complications – Visual Studio added a reference to the 64 bit Oracle.DataAccess.dll.  As a result, I received an error similar to “Data provider internal error(-3000)”.  Completely fixing this issue required a little bit of work, and might require a different procedure on your computer.  First, I had to select projectname Properties from the Project menu and change the Platform target from Any CPU to x86:

SeeSharpObjectsPropertiesPlatform

I then saw the following warning in Visual Studio: “Assembly generation — Referenced assembly ‘Oracle.DataAccess.dll’ targets a different processor”.  The properties for the Oracle.DataAccess reference in the Solution Explorer showed that the Path property pointed to the 64 bit DLL.  In my case, the 32 bit DLL was in a different Oracle home, specifically located at C:\Oracle\product\11.2.0\client_1\ODP.NET\bin\2.x\Oracle.DataAccess.dll – I did not see any simple method to quickly reference the 32 bit DLL, rather than the 64 bit DLL.  So, I right-clicked Oracle.DataAccess in the References list and selected Remove from the menu.  To add the reference to the 32 bit DLL, I selected Add Reference… from the Project menu, selected the Browse tab, navigated to the C:\Oracle\product\11.2.0\client_1\ODP.NET\bin\2.x folder (the 32 bit ODP.NET driver may be in a different folder on your computer), selected the Oracle.DataAccess.dll file and then clicked OK:

SeeSharpObjectsBrowseReference

The above steps fixed the problems, and allowed the C# program to connect to the database (I thought that .Net was supposed to mark the end of DLL Hell?).  Moving on…

Next, I want to set up ADO Command objects in the VB 6.0 project and OracleCommand objects in the C# project to insert into the three test tables, and to execute a SELECT statement.  Since the routines for inserting into the tables and selecting the data could be called many times in the program, I only want to set up these command objects once, and then use those command objects multiple times.  In the General section of the VB 6.0 form code (above the Private Sub Form_Load() ) line, add the following to create four ADO command objects:

Dim comInsertPO As ADODB.Command
Dim comInsertPOLine As ADODB.Command
Dim comInsertPOTrans As ADODB.Command
Dim comSelectPO As ADODB.Command

In the C# form code, below the opening bracket of the line “public partial class frmSeeSharpObjects : Form“, add the following lines (note, frmSeeSharpObjects is the name of the form that I created for this project):

        OracleCommand comInsertPO;
        OracleCommand comInsertPOLine;
        OracleCommand comInsertPOTrans;
        OracleCommand comSelectPO;

In the VB 6.0 form code, just before the End Sub in the Form_Load() event procedure, we will add additional code to create and configure the four ADO command objects.  ADO uses ? characters as bind variable placeholders:

    Set comInsertPO = New ADODB.Command
    With comInsertPO
        strSQL = "INSERT INTO PO(" & vbCrLf
        strSQL = strSQL & "  ORDER_ID," & vbCrLf
        strSQL = strSQL & "  VENDOR_ID," & vbCrLf
        strSQL = strSQL & "  ORDER_DATE," & vbCrLf
        strSQL = strSQL & "  SHIP_TO_ID)" & vbCrLf
        strSQL = strSQL & "VALUES(" & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?)"
    
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbConnection
        'Add the bind variables
        .Parameters.Append .CreateParameter("order_id", adVarChar, adParamInput, 15, "12345678910")
        .Parameters.Append .CreateParameter("vendor_id", adVarChar, adParamInput, 15, "ABCDEFGHIJKLM")
        .Parameters.Append .CreateParameter("order_date", adDate, adParamInput, 8, Now)
        .Parameters.Append .CreateParameter("ship_to_id", adNumeric, adParamInput, 8, 0)
    End With
        
    Set comInsertPOLine = New ADODB.Command
    With comInsertPOLine
        strSQL = "INSERT INTO PO_LINE(" & vbCrLf
        strSQL = strSQL & "  ORDER_ID," & vbCrLf
        strSQL = strSQL & "  LINE_NO," & vbCrLf
        strSQL = strSQL & "  PART_ID," & vbCrLf
        strSQL = strSQL & "  QTY," & vbCrLf
        strSQL = strSQL & "  PRICE," & vbCrLf
        strSQL = strSQL & "  DUE_DATE)" & vbCrLf
        strSQL = strSQL & "VALUES(" & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?)"
    
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbConnection
        'Add the bind variables
        .Parameters.Append .CreateParameter("order_id", adVarChar, adParamInput, 15, "12345678910")
        .Parameters.Append .CreateParameter("line_no", adNumeric, adParamInput, 8, 1)
        .Parameters.Append .CreateParameter("part_id", adVarChar, adParamInput, 15, "ABCDEFGHIJKLM")
        .Parameters.Append .CreateParameter("qty", adDouble, adParamInput, 15, 1)
        .Parameters.Append .CreateParameter("price", adDouble, adParamInput, 15, 1)
        .Parameters.Append .CreateParameter("due_date", adDate, adParamInput, 15)
    End With
    
    Set comInsertPOTrans = New ADODB.Command
    With comInsertPOTrans
        strSQL = "INSERT INTO PO_TRANS(" & vbCrLf
        strSQL = strSQL & "  TRANSACTION_ID," & vbCrLf
        strSQL = strSQL & "  VENDOR_ID," & vbCrLf
        strSQL = strSQL & "  PART_ID," & vbCrLf
        strSQL = strSQL & "  QTY," & vbCrLf
        strSQL = strSQL & "  DUE_DATE," & vbCrLf
        strSQL = strSQL & "  UNKNOWN)" & vbCrLf
        strSQL = strSQL & "VALUES(" & vbCrLf
        strSQL = strSQL & "  PO_TRANS_ID.NEXTVAL," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?," & vbCrLf
        strSQL = strSQL & "  ?)"
   
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbConnection
        'Add the bind variables
        .Parameters.Append .CreateParameter("vendor_id", adVarChar, adParamInput, 15, "ABCDEFGHIJKLM")
        .Parameters.Append .CreateParameter("part_id", adVarChar, adParamInput, 15, "ABCDEFGHIJKLM")
        .Parameters.Append .CreateParameter("qty", adDouble, adParamInput, 15, 1)
        .Parameters.Append .CreateParameter("due_date", adDate, adParamInput, 15)
        .Parameters.Append .CreateParameter("unknown", adDouble, adParamInput, 15, 1)
    End With
    
    Set comSelectPO = New ADODB.Command
    With comSelectPO
        strSQL = "SELECT" & vbCrLf
        strSQL = strSQL & "  P.VENDOR_ID," & vbCrLf
        strSQL = strSQL & "  PL.PART_ID," & vbCrLf
        strSQL = strSQL & "  PL.QTY," & vbCrLf
        strSQL = strSQL & "  PL.DUE_DATE" & vbCrLf
        strSQL = strSQL & "FROM" & vbCrLf
        strSQL = strSQL & "  PO P," & vbCrLf
        strSQL = strSQL & "  PO_LINE PL" & vbCrLf
        strSQL = strSQL & "WHERE" & vbCrLf
        strSQL = strSQL & "  P.ORDER_ID=PL.ORDER_ID" & vbCrLf
        strSQL = strSQL & "  AND P.ORDER_DATE>=TRUNC(SYSDATE-30)" & vbCrLf
        strSQL = strSQL & "ORDER BY" & vbCrLf
        strSQL = strSQL & "  P.VENDOR_ID," & vbCrLf
        strSQL = strSQL & "  PL.PART_ID"
        
        .CommandText = strSQL
        .CommandType = adCmdText
        .CommandTimeout = 30
        .ActiveConnection = dbConnection
    End With

Now to add the equivalent code to the _Load event procedure code in C# just before the closing } of the procedure (ODP.NET uses : followed by an identifier as bind variable placeholders):

strSQL = "INSERT INTO PO(" + "\n";
strSQL = strSQL + " ORDER_ID," + "\n";
strSQL = strSQL + " VENDOR_ID," + "\n";
strSQL = strSQL + " ORDER_DATE," + "\n";
strSQL = strSQL + " SHIP_TO_ID)" + "\n";
strSQL = strSQL + "VALUES(" + "\n";
strSQL = strSQL + " :1," + "\n";
strSQL = strSQL + " :2," + "\n";
strSQL = strSQL + " :3," + "\n";
strSQL = strSQL + " :4)";
 
comInsertPO = new OracleCommand(strSQL, Global.dbConnection);
//comInsertPO.CommandText = strSQL;
comInsertPO.CommandType = CommandType.Text;
comInsertPO.CommandTimeout = 30;
comInsertPO.BindByName = false;
 
//Add the bind variables
comInsertPO.Parameters.Add("order_id", OracleDbType.Varchar2, 15, "12345678910", ParameterDirection.Input);
comInsertPO.Parameters.Add("vendor_id", OracleDbType.Varchar2, 15, "ABCDEFGHIJKLM", ParameterDirection.Input);
comInsertPO.Parameters.Add("order_date", OracleDbType.Date, 8, DateTime.Now, ParameterDirection.Input);
comInsertPO.Parameters.Add("ship_to_id", OracleDbType.Long, 8, 0, ParameterDirection.Input);
 
strSQL = "INSERT INTO PO_LINE(" + "\n";
strSQL = strSQL + " ORDER_ID," + "\n";
strSQL = strSQL + " LINE_NO," + "\n";
strSQL = strSQL + " PART_ID," + "\n";
strSQL = strSQL + " QTY," + "\n";
strSQL = strSQL + " PRICE," + "\n";
strSQL = strSQL + " DUE_DATE)" + "\n";
strSQL = strSQL + "VALUES(" + "\n";
strSQL = strSQL + " :1," + "\n";
strSQL = strSQL + " :2," + "\n";
strSQL = strSQL + " :3," + "\n";
strSQL = strSQL + " :4," + "\n";
strSQL = strSQL + " :5," + "\n";
strSQL = strSQL + " :6)";
 
comInsertPOLine = new OracleCommand(strSQL, Global.dbConnection);
//comInsertPOLine.CommandText = strSQL;
comInsertPOLine.CommandType = CommandType.Text;
comInsertPOLine.CommandTimeout = 30;
comInsertPOLine.BindByName = false;
 
//Add the bind variables
comInsertPOLine.Parameters.Add("order_id", OracleDbType.Varchar2, 15, "12345678910", ParameterDirection.Input);
comInsertPOLine.Parameters.Add("line_no", OracleDbType.Long, 8, 1, ParameterDirection.Input);
comInsertPOLine.Parameters.Add("part_id", OracleDbType.Varchar2, 15, "ABCDEFGHIJKLM", ParameterDirection.Input);
comInsertPOLine.Parameters.Add("qty", OracleDbType.Double, 15, 1, ParameterDirection.Input);
comInsertPOLine.Parameters.Add("price", OracleDbType.Double, 15, 1, ParameterDirection.Input);
comInsertPOLine.Parameters.Add("due_date", OracleDbType.Date, 15, ParameterDirection.Input);
 
strSQL = "INSERT INTO PO_TRANS(" + "\n";
strSQL = strSQL + " TRANSACTION_ID," + "\n";
strSQL = strSQL + " VENDOR_ID," + "\n";
strSQL = strSQL + " PART_ID," + "\n";
strSQL = strSQL + " QTY," + "\n";
strSQL = strSQL + " DUE_DATE," + "\n";
strSQL = strSQL + " UNKNOWN)" + "\n";
strSQL = strSQL + "VALUES(" + "\n";
strSQL = strSQL + " PO_TRANS_ID.NEXTVAL," + "\n";
strSQL = strSQL + " :1," + "\n";
strSQL = strSQL + " :2," + "\n";
strSQL = strSQL + " :3," + "\n";
strSQL = strSQL + " :4," + "\n";
strSQL = strSQL + " :5)";
 
comInsertPOTrans = new OracleCommand(strSQL, Global.dbConnection);
//comInsertPOTrans.CommandText = strSQL;
comInsertPOTrans.CommandType = CommandType.Text;
comInsertPOTrans.CommandTimeout = 30;
comInsertPOTrans.BindByName = false;
 
//Add the bind variables
comInsertPOTrans.Parameters.Add("vendor_id", OracleDbType.Varchar2, 15, "ABCDEFGHIJKLM", ParameterDirection.Input);
comInsertPOTrans.Parameters.Add("part_id", OracleDbType.Varchar2, 15, "ABCDEFGHIJKLM", ParameterDirection.Input);
comInsertPOTrans.Parameters.Add("qty", OracleDbType.Double, 15, 1, ParameterDirection.Input);
comInsertPOTrans.Parameters.Add("due_date", OracleDbType.Date, 15, ParameterDirection.Input);
comInsertPOTrans.Parameters.Add("unknown", OracleDbType.Double, 15, 1, ParameterDirection.Input);
 
strSQL = "SELECT" + "\n";
strSQL = strSQL + " P.VENDOR_ID," + "\n";
strSQL = strSQL + " PL.PART_ID," + "\n";
strSQL = strSQL + " PL.QTY," + "\n";
strSQL = strSQL + " PL.DUE_DATE" + "\n";
strSQL = strSQL + "FROM" + "\n";
strSQL = strSQL + " PO P," + "\n";
strSQL = strSQL + " PO_LINE PL" + "\n";
strSQL = strSQL + "WHERE" + "\n";
strSQL = strSQL + " P.ORDER_ID=PL.ORDER_ID" + "\n";
strSQL = strSQL + " AND P.ORDER_DATE>=TRUNC(SYSDATE-30)" + "\n";
strSQL = strSQL + "ORDER BY" + "\n";
strSQL = strSQL + " P.VENDOR_ID," + "\n";
strSQL = strSQL + " PL.PART_ID";
 
comSelectPO = new OracleCommand(strSQL, Global.dbConnection);
//comSelectPO.CommandText = strSQL;
comSelectPO.CommandType = CommandType.Text;
comSelectPO.CommandTimeout = 30;

It should be possible to run the program now without generating errors, but the program will not do much other than connect to the database at this point.  Now that the basic setup is out of the way, we will create a Command Button in VB 6.0 (a Button in C#) and set its name to cmdProcess.  In VB 6.0, double-click the command button to access the Click event code for the command button.  Add the following code, which will add a few rows to the PO and PO_LINE tables, select some of the rows from those tables with a join on the ORDER_ID column in each table, and then insert rows into the PO_TRANS table using the SELECT resultset.  Note that it is possible to generate a 10046 trace at level 4 (with bind variables) by removing the character on the line below the On Error Resume Next line.  Note too that it is only necessary to specify the value of bind variables if those values must change after each INSERT is executed:

Private Sub cmdProcess_Click()
    Dim snpData As ADODB.Recordset
    
    On Error Resume Next
    
    'dbConnection.Execute "ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER, LEVEL 4'"
    
    dbConnection.BeginTrans
    
    comInsertPO("order_id") = "A10000"
    comInsertPO("vendor_id") = "BUBBA GUMP"
    comInsertPO("order_date") = DateAdd("d", -62, Date)
    comInsertPO("ship_to_id") = 1
    comInsertPO.Execute
    
    comInsertPO("order_id") = "A10001"
    comInsertPO("order_date") = DateAdd("d", -25, Date)
    comInsertPO.Execute
    
    comInsertPO("order_id") = "A10002"
    comInsertPO("vendor_id") = "POPPA GUMP"
    comInsertPO("order_date") = DateAdd("d", -20, Date)
    comInsertPO("ship_to_id") = 1
    comInsertPO.Execute
    
    comInsertPO("order_id") = "A10003"
    comInsertPO("order_date") = DateAdd("d", -2, Date)
    comInsertPO("ship_to_id") = 1
    comInsertPO.Execute
    
    comInsertPO("order_id") = "A10004"
    comInsertPO("vendor_id") = "POPPA SMURF"
    comInsertPO("order_date") = DateAdd("d", -1, Date)
    comInsertPO("ship_to_id") = 1
    comInsertPO.Execute
    
    comInsertPOLine("order_id") = "A10000"
    comInsertPOLine("line_no") = 1
    comInsertPOLine("part_id") = "SPAM"
    comInsertPOLine("qty") = 5
    comInsertPOLine("price") = 10.25
    comInsertPOLine("due_date") = DateAdd("d", -1, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("line_no") = 2
    comInsertPOLine("due_date") = DateAdd("d", 3, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("line_no") = 3
    comInsertPOLine("due_date") = DateAdd("d", 6, Date)
    comInsertPOLine.Execute
    comInsertPOLine("order_id") = "A10001"
    comInsertPOLine("line_no") = 1
    comInsertPOLine("part_id") = "STEAK"
    comInsertPOLine("qty") = 3
    comInsertPOLine("price") = 21.85
    comInsertPOLine("due_date") = DateAdd("d", -20, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("line_no") = 2
    comInsertPOLine("qty") = 4
    comInsertPOLine("due_date") = DateAdd("d", 10, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("line_no") = 3
    comInsertPOLine("qty") = 3
    comInsertPOLine("due_date") = DateAdd("d", 15, Date)
    comInsertPOLine.Execute
    comInsertPOLine("order_id") = "A10002"
    comInsertPOLine("line_no") = 1
    comInsertPOLine("part_id") = "CHUCKBURGER"
    comInsertPOLine("qty") = 3
    comInsertPOLine("price") = 15.01
    comInsertPOLine("due_date") = DateAdd("d", 10, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("order_id") = "A10003"
    comInsertPOLine("line_no") = 1
    comInsertPOLine("part_id") = "BACON"
    comInsertPOLine("qty") = 3
    comInsertPOLine("price") = 16.49
    comInsertPOLine("due_date") = DateAdd("d", 9, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("line_no") = 2
    comInsertPOLine("part_id") = "EGGS"
    comInsertPOLine("qty") = 19
    comInsertPOLine("price") = 0.49
    comInsertPOLine("due_date") = DateAdd("d", 9, Date)
    comInsertPOLine.Execute
    
    comInsertPOLine("order_id") = "A10004"
    comInsertPOLine("line_no") = 1
    comInsertPOLine("part_id") = "ROASTBEEF"
    comInsertPOLine("qty") = 200
    comInsertPOLine("price") = 11.99
    comInsertPOLine("due_date") = DateAdd("d", 30, Date)
    comInsertPOLine.Execute
    Set snpData = comSelectPO.Execute
    
    If (snpData.State <> 1) Then
        MsgBox "SQL statement did not execute."
    Else
        If (snpData.EOF = True) Then
            MsgBox "No rows returned from the database."
        Else
            Do While Not (snpData.EOF)
                comInsertPOTrans("vendor_id") = snpData("vendor_id")
                comInsertPOTrans("part_id") = snpData("part_id")
                comInsertPOTrans("qty") = snpData("qty") * 1.5
                comInsertPOTrans("due_date") = DateAdd("m", -1, snpData("due_date"))
                comInsertPOTrans("unknown") = Rnd * 100
                comInsertPOTrans.Execute
                
                snpData.MoveNext
            Loop
        End If
        
        snpData.Close
    End If
    
    If Err = 0 Then
        dbConnection.CommitTrans
        
        MsgBox "Processing finished successfully."
    Else
        dbConnection.RollbackTrans
        
        MsgBox "Could not update the database due to an error." & vbCrLf & Error
    End If
End Sub

Now to add the nearly equivalent C# code – the only major difference is that the C# code jumps out of the try block to the catch block when the first error is encountered, while in the VB 6.0 code, due to the usage of the On Error Resume Next command, the processing simply continues when an error occurs.  Note that it is possible to generate a 10046 trace at level 4 (with bind variables) by removing the // characters on the two lines just after the try block begins:

private void cmdProcess_Click(object sender, EventArgs e)
{
    Random rndSequence = new Random();
    OracleDataReader snpData;
    OracleTransaction oraTransaction;
    oraTransaction = Global.dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
 
    try
    {
        //OracleCommand comTrace = new OracleCommand("ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER, LEVEL 4'", Global.dbConnection);
        //comTrace.ExecuteNonQuery();
        
        comInsertPO.Parameters["order_id"].Value = "A10000";
        comInsertPO.Parameters["vendor_id"].Value = "BUBBA GUMP";
        comInsertPO.Parameters["order_date"].Value = DateTime.Today.AddDays(-62);
        comInsertPO.Parameters["ship_to_id"].Value = 1;
        comInsertPO.ExecuteNonQuery();
        
        comInsertPO.Parameters["order_id"].Value = "A10001";
        comInsertPO.Parameters["order_date"].Value = DateTime.Today.AddDays(-25);
        comInsertPO.ExecuteNonQuery();
        
        comInsertPO.Parameters["order_id"].Value = "A10002";
        comInsertPO.Parameters["vendor_id"].Value = "POPPA GUMP";
        comInsertPO.Parameters["order_date"].Value = DateTime.Today.AddDays(-20);
        comInsertPO.Parameters["ship_to_id"].Value = 1;
        comInsertPO.ExecuteNonQuery();
        
        comInsertPO.Parameters["order_id"].Value = "A10003";
        comInsertPO.Parameters["order_date"].Value = DateTime.Today.AddDays(-2);
        comInsertPO.Parameters["ship_to_id"].Value = 1;
        comInsertPO.ExecuteNonQuery();
        
        comInsertPO.Parameters["order_id"].Value = "A10004";
        comInsertPO.Parameters["vendor_id"].Value = "POPPA SMURF";
        comInsertPO.Parameters["order_date"].Value = DateTime.Today.AddDays(-1);
        comInsertPO.Parameters["ship_to_id"].Value = 1;
        comInsertPO.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["order_id"].Value = "A10000";
        comInsertPOLine.Parameters["line_no"].Value = 1;
        comInsertPOLine.Parameters["part_id"].Value = "SPAM";
        comInsertPOLine.Parameters["qty"].Value = 5;
        comInsertPOLine.Parameters["price"].Value = 10.25;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(-1);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["line_no"].Value = 2;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(3);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["line_no"].Value = 3;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(6);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["order_id"].Value = "A10001";
        comInsertPOLine.Parameters["line_no"].Value = 1;
        comInsertPOLine.Parameters["part_id"].Value = "STEAK";
        comInsertPOLine.Parameters["qty"].Value = 3;
        comInsertPOLine.Parameters["price"].Value = 21.85;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(-20);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["line_no"].Value = 2;
        comInsertPOLine.Parameters["qty"].Value = 4;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(10);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["line_no"].Value = 3;
        comInsertPOLine.Parameters["qty"].Value = 3;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(15);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["order_id"].Value = "A10002";
        comInsertPOLine.Parameters["line_no"].Value = 1;
        comInsertPOLine.Parameters["part_id"].Value = "CHUCKBURGER";
        comInsertPOLine.Parameters["qty"].Value = 3;
        comInsertPOLine.Parameters["price"].Value = 15.01;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(10);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["order_id"].Value = "A10003";
        comInsertPOLine.Parameters["line_no"].Value = 1;
        comInsertPOLine.Parameters["part_id"].Value = "BACON";
        comInsertPOLine.Parameters["qty"].Value = 3;
        comInsertPOLine.Parameters["price"].Value = 16.49;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(9);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["line_no"].Value = 2;
        comInsertPOLine.Parameters["part_id"].Value = "EGGS";
        comInsertPOLine.Parameters["qty"].Value = 19;
        comInsertPOLine.Parameters["price"].Value = 0.49;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(9);
        comInsertPOLine.ExecuteNonQuery();
        
        comInsertPOLine.Parameters["order_id"].Value = "A10004";
        comInsertPOLine.Parameters["line_no"].Value = 1;
        comInsertPOLine.Parameters["part_id"].Value = "ROASTBEEF";
        comInsertPOLine.Parameters["qty"].Value = 200;
        comInsertPOLine.Parameters["price"].Value = 11.99;
        comInsertPOLine.Parameters["due_date"].Value = DateTime.Today.AddDays(30);
        comInsertPOLine.ExecuteNonQuery();
        
        snpData = comSelectPO.ExecuteReader();
        if (snpData.IsClosed)
        {
            MessageBox.Show("SQL statement did not execute.");
        }
        else
        {
            if (!snpData.HasRows)
            {
                MessageBox.Show("No rows returned from the database.");
            }
            else
            {
                //snpData.FetchSize = comSelectPO.RowSize * 100;
                while (snpData.Read())
                {
                    comInsertPOTrans.Parameters["vendor_id"].Value = snpData["vendor_id"];
                    comInsertPOTrans.Parameters["part_id"].Value = snpData["part_id"];
                    comInsertPOTrans.Parameters["qty"].Value = Convert.ToDouble(snpData["qty"]) * 1.5;
                    comInsertPOTrans.Parameters["due_date"].Value = Convert.ToDateTime(snpData["due_date"]).AddMonths(-1);
                    comInsertPOTrans.Parameters["unknown"].Value = rndSequence.NextDouble() * 100
                    comInsertPOTrans.ExecuteNonQuery();
                 } //while (snpData.Read())
            } //else of if (!snpData.HasRows)
 
            snpData.Close();
        }
        oraTransaction.Commit();
        MessageBox.Show("Processing finished successfully.");
    }
    catch (Exception ex)
    {
        oraTransaction.Rollback();
        MessageBox.Show("Could not update the database due to an error." + "\n" + ex.Message.ToString());
    }
}

With the above code in place, it should be possible to run the program and click the command button on the form to insert rows into the PO, PO_LINE, and PO_TRANS tables.  Of course, it is only possible to click that command button once without an error appearing on screen in either the VB 6.0 or C# programs, respectively:
SeeSharpObjectsUniqueConstraintVB  SeeSharpObjectsUniqueConstraint

To re-execute the code in the command button’s Click event, the tables must first be cleared of rows using SQL*Plus:

TRUNCATE TABLE PO;
TRUNCATE TABLE PO_LINE;
TRUNCATE TABLE PO_TRANS;

As a test, I executed the cmdProcess button in VB 6.0, and then the following SQL statement in SQL*Plus to see what ended up in the PO_TRANS table:

SELECT
  *
FROM
  PO_TRANS
ORDER BY
  TRANSACTION_ID;

Here is the output that I received (notice, no SPAM) from the processing in VB 6.0:

TRANSACTION_ID VENDOR_ID       PART_ID            QTY DUE_DATE     UNKNOWN
-------------- --------------- ----------- ---------- --------- ----------
          1057 BUBBA GUMP      STEAK              4.5 15-OCT-15    70.5547
          1058 BUBBA GUMP      STEAK                6 15-NOV-15    53.3424
          1059 BUBBA GUMP      STEAK              4.5 20-NOV-15    57.9519
          1060 POPPA GUMP      BACON              4.5 14-NOV-15    28.9562
          1061 POPPA GUMP      CHUCKBURGER        4.5 15-NOV-15    30.1948
          1062 POPPA GUMP      EGGS              28.5 14-NOV-15     77.474
          1063 POPPA SMURF     ROASTBEEF          300 04-DEC-15     1.4018

I then truncated the three tables and executed the cmdProcess button in C#.  Here is the output of the above SQL statement after executing the cmdProcess button in C#:

TRANSACTION_ID VENDOR_ID       PART_ID            QTY DUE_DATE     UNKNOWN
-------------- --------------- ----------- ---------- --------- ----------
          1064 BUBBA GUMP      STEAK              4.5 15-OCT-15    55.5915
          1065 BUBBA GUMP      STEAK                6 15-NOV-15    65.7347
          1066 BUBBA GUMP      STEAK              4.5 20-NOV-15    83.0905
          1067 POPPA GUMP      BACON              4.5 14-NOV-15    19.0149
          1068 POPPA GUMP      CHUCKBURGER        4.5 15-NOV-15    73.2245
          1069 POPPA GUMP      EGGS              28.5 14-NOV-15    61.9526
          1070 POPPA SMURF     ROASTBEEF          300 04-DEC-15    93.9571

The output is identical, with the exception of the UNKNOWN column, which is a pseudo-random number between 0 and 99.9999.  A job well done, with one complication.  What is the complication?  Think about the code logic for a minute before scrolling down.

Thought about the complication?  No, it is not that this blog article required approximately 11 hours to write (a large percentage of that time is due to WordPress’ auto-formatting of the article).  The code violates the fifth bullet point that is listed at the start of the article.  The insert into the PO_TRANS table could have been performed by sending a single SQL statement to the database, rather than using a while or Do While loop and performing an execution of the INSERT statement for each row in the resultset.

If you would like to use a free version of C#, Microsoft offers the Express edition with no apparent licensing restrictions and a more powerful Community edition that carries licensing restrictions.  Both versions may be downloaded from Microsoft’s website.

On a remotely related note, a week ago a Microsoft Windows technician called about a computer problem that I was having.  My first thought was, “how did he know I was translating a VB 6.0 program to C#?”  My second thought was, “which problem?”  Sadly, I immediately recognized this call as one of those “Microsoft” support scam calls, and decided that the person on the other end of the line was not equipped to walk with, let along run with, sharp objects.  As a result, I decided to terminate the call rather quickly:
“Microsoft” support scammer: I am calling about a problem with your computer.
Me: A computer? (in a concerned voice)
“Microsoft” support scammer: Yes, your laptop computer.
Me: I don’t have a laptop computer.
“Microsoft” support scammer: Oh, your Windows tower computer.
Me: I don’t have a computer.
“Microsoft” support scammer: (Click)

The scammer gave up a little too easily – I had planned to ask her if she knew how to troubleshoot sticky key problems on my typewriter.  That is the second Microsoft tech that gave up before we had a chance to troubleshoot the virtual machine.





Web Pages Not Databases – Part 2: Fail2ban, Apache, IP Addresses, Linux, SELinux

23 08 2015

August 23, 2015 (Modified August 31, 2015, September 14, 2015)

(Back to the Previous Article in this Series)

I started using Linux in 1999, specifically Red Hat Linux 6.0, and I recall upgrading to Red Hat Linux 6.1 after downloading the files over a 56k modem – the good old days.  I was a little more wise when I upgraded to another release a couple of months later – I found a site on the Internet that offered Red Hat Linux CD sets for a couple of dollars.  In late 2001/early 2002 I picked up a very good book about creating Linux-based IPTables firewalls, so I set up a dual firewall setup (with a DMZ in between) using a couple of spare computers.  That setup worked great in a corporate environment for several years – I even upgraded the hardware in 2006 to inexpensive Dell PowerEdge servers and installed the latest version of Red Hat Linux (I believe Fedora 5).  I was excited about the potential capabilities of this free operating system, even going so far in 2004 to use it as the operating system for the primary file servers (Red Hat Enterprise Linux 3, if I remember correctly) in an effort to save a few thousand dollars in Microsoft licensing fees (it almost worked too).

F.A.I.L.S.?  I must have put those keywords in the blog article title for a reason, or maybe not.  In 2003 I tried setting up the Frees/wan VPN server on a spare Linux computer as an alternative to having to use a 28k/33k dial up modem connection.  It was around that time that I learned the dark side of Linux and the “free” software that could be installed.  I found an old message thread that I posted in 2003 related to Frees/wan where I mentioned that I spent in excess of 2.5 months trying to make this free VPN solution work correctly.  There were several how-to articles returned by a Google search, some of which were written for other Linux variants, others did not use X.509 certificates, and others almost worked.  Making matters worse, the Red Hat Linux kernel at the time did not support X.509 certificates, so I eventually ended up installing the Working Overloaded Linux Kernel.  I recall desperately looking for a program called Setup.exe that would just take care of the problem, but no such program was found.  A couple of months after I had Frees/wan working, a security compromise was reported in all products like Frees/wan, and the Frees/wan development had been abandoned.  I learned a very important lesson that “free” software may not be free software when you consider the time that it takes to implement and maintain the free software.  I also learned another important lesson – Linux how-to articles that are more than a couple of months old may be misleading or nearly useless; Linux articles that are written for one of the other 790 Linux Distributions may be just as misleading or useless; and not everything on the Internet in a hot-to article is true/correct (this article is no exception).

With that long introduction out of the way, I thought that I would share a couple of notes that I collected along the way when I setup Fedora 22 Linux as a server for a website that uses Apache and WordPress.  I have the headache inspiring SELinux enabled on the server, as well as the latest version of Fail2ban to temporarily block IP addresses used by the clowns on the Internet that want to make the Linux server running WordPress their new best friend.  So far, Fail2ban is working great, once the how-to articles that apply to Fedora 21 or Fedora 20 are ignored, although the current version does output apparently incorrect error messages when certain commands are executed:

[fedora 22]# fail2ban-client reload wordpress-login
ERROR  NOK: ('Cannot change database when there are jails present',)

Protecting Fedora 22 Linux with a Firewall

In one of the recent 17 Fedora releases, there was a transition from directly calling iptables commands in a script to using a command called firewall-cmd to accomplish the same task.  So, on Fedora 22 you should no longer execute commands like this:

iptables -t nat -A PREROUTING -i $INET_INTERFACE -p esp -j DNAT --to $VPN_IPADDR
 
iptables -A FORWARD -i $INET_INTERFACE -o $DMZ_INTERFACE -p udp --sport 4500 --dport 4500 -d $VPN_IPADDR -j ACCEPT
 
iptables -A FORWARD -i $INET_INTERFACE -o $DMZ_INTERFACE -p esp -j ACCEPT

Instead, with Fedora 22 the commands that are used to control the firewall have an entirely different syntax (allow access to port http 80, https port 443, ssh port 22, and ftp ports 20/21, remove access to FTP ports 20/21, and then reload and activate the changed rules):

firewall-cmd --set-default-zone=public 
 
firewall-cmd --permanent --zone=public --add-service=http 
 
firewall-cmd --permanent --zone=public --add-service=https 
 
firewall-cmd --permanent --zone=public --add-service=ssh
 
firewall-cmd --permanent --zone=public --add-service=ftp
 
firewall-cmd --permanent --zone=public --remove-service=ftp
 
firewall-cmd --reload

The changes do not take effect until the reload command is executed.  If you are planning to setup a publically accessible website, and you do not want the server to respond to ping requests and similar icmp requests, you might add a couple of additional firewall rules:

firewall-cmd --permanent --zone=public --add-icmp-block=destination-unreachable
firewall-cmd --permanent --zone=public --add-icmp-block=echo-reply
firewall-cmd --permanent --zone=public --add-icmp-block=echo-request
firewall-cmd --permanent --zone=public --add-icmp-block=parameter-problem
firewall-cmd --permanent --zone=public --add-icmp-block=redirect
firewall-cmd --permanent --zone=public --add-icmp-block=router-advertisement
firewall-cmd --permanent --zone=public --add-icmp-block=router-solicitation
firewall-cmd --permanent --zone=public --add-icmp-block=source-quench
firewall-cmd --permanent --zone=public --add-icmp-block=time-exceeded
firewall-cmd --reload

You might also decide to block certain web content spiders that mercilessly drain your server’s Internet bandwidth without returning any benefit to your website.  I noticed that the Baiduspider web crawler is a frequent offender, using several ranges of IP addresses.  I put an end to a large portion of the bandwidth drain from this web content spider with a simple firewall rule that blocks the IP address range 180.76.15.1 through 180.76.15.254 (don’t forget to reload after):

firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='180.76.15.0/24' reject"

Note that you may see a message similar to the following when attempting to execute the reload command:

Error: 'NoneType' object has no attribute 'query_rule'

If you see the above error message when trying to reload the firewall rules, just shout “free Linux software” five times and execute this command to restart the firewall – this command should have the same end effect as the reload command, except that this command works:

systemctl restart firewalld

Now, assume that you have setup Fail2ban’s ssh jail.  After a couple of hours you have received over 200 emails from Fail2ban telling you that it has blocked 200+ computers wanting to be best ssh friends with your server.  Obviously, you skipped the step of setting up a different port for ssh.  Modify the sshd config file (if you forgot the basic vi commands: press i to be able to make changes in the file, Esc ZZ to save the changes and exit, Esc :q! to quit without saving changes):

vi /etc/ssh/sshd_config

Assume that you want to change the ssh port from 22 to 1492 (something about sailing the ocean blue?).  Below the #Port 22 heading, add:

Port 1492

Then save the file and exit vi.  Since SELinux is enabled, we need to instruct SELinux to behave correctly when an ssh client attaches to port 1492:

semanage port -a -t ssh_port_t -p tcp 1492

Note: Using the semanage command requires another package to be installed first:

dnf install policycoreutils-python

Note 2: If you think that SELinux is blocking something that should not be blocked, SELinux may be temporarily disabled with this command:

setenforce 0

To re-enable SELinux, either reboot the server or execute this command:

setenforce 1

Next, we need to add a firewall rule to permit connections on port 1492, and reload the firewall rules (note that I am using the command to restart the firewall daemon instead due to the error that appeared with the reload command):

firewall-cmd --permanent --zone=public  --add-port=1492/tcp
systemctl restart firewalld

As a final verification, make certain that the Linux firewall and SELinux recognize the new port:

firewall-cmd --list-ports
semanage port -l | grep ssh

If there are no apparent problems with the above output, restart the ssh daemon:

systemctl reload sshd.service

You may also wish to confirm which services are enabled for the Linux firewall:

firewall-cmd --list-services

Beating on a Linux box that lacks a monitor and keyboard is only so much fun (that old reboot joke, I guess).  If you have a Windows computer handy, the free Putty program will allow access to the ssh interface on the Linux server.  WinSCP is a helpful utility that provides Windows Explorer-like views through the ssh interface on the Linux server.

Protecting Fedora 22 Linux with Fail2ban

Fail2ban is a utility that monitors various log files on the server, looking for unexpected activity that typically originates from another computer on the network or on the Internet.  Fail2ban may be setup to take various actions when a problem is noticed, such as the same IP address failing to connect to SSH 10 times in 15 minutes.  The action may be to send an email to an administrator and/or to configure a firewall rule that temporarily blocks the offender’s IP address.  There are a few how-to articles found through Google searches that describe how to install and configure Fail2ban.  Shockingly (not really), some of those articles are more than a couple of months old (so the articles may not work with Fedora 22) and/or instruct people to modify files that explicitly state in the header:

# YOU SHOULD NOT MODIFY THIS FILE.

What to do?  What to do?

If you have not done so recently, make certain that the installed Fedora packages are up to date (dfn… another new command, what happened to the rpm command?):

dnf update

If the Apache web server is running on the server, there is a good chance that you execute commands similar to the following at some time in the past:

dnf install httpd
systemctl start httpd.service
systemctl enable httpd.service

Fail2ban is able to send emails using Sendmail, so if Sendmail is not installed, consider installing it:

dnf install sendmail
systemctl start sendmail
systemctl enable sendmail

While not directly applying to Fail2ban, SELinux, by default, blocks Apache from using Sendmail.  It is possible to verify that this is the case, and remove the restriction with these two commands:

sestatus -b | grep -i sendmail
setsebool -P httpd_can_sendmail 1

With Sendmail installed and running, we are able to proceed with the Fail2ban installation and configuration:

dnf install fail2ban ipset
dnf install whois fail2ban-sendmail
systemctl start fail2ban
systemctl enable fail2ban

The configuration file for Fail2ban that should be modified is /etc/fail2ban/jail.d/local.conf – but that file does not exist after installation.  The local.conf file references files in the /etc/fail2ban/filter.d/ directory that tell Fail2ban how to read the various log files and recognize problems using regular expressions (they look pretty irregular to me, but then I have not done much with regular expressions since that Turbo Pascal programming class years ago).  A starting point for the local.conf file with Fedora 22 and Sendmail, blocking ssh connection requests after a few incorrect login attempts from the same IP address within an hour, would look like the following (replace my.IP.address.here with your IP address so that Fail2ban will ignore your incorrect login attempts):

[DEFAULT]
bantime = 2592000
banaction = firewallcmd-ipset
backend = systemd
sender = emailaddress1@mydomain.com
destemail = emailaddress2@mydomain.com
action = %(action_mwl)s
ignoreip = 127.0.0.1 my.IP.address.here
 
[sshd]
enabled = true
findtime = 3600

The settings listed under the [DEFAULT] heading apply to all of the other sections in this file, unless those settings are also mentioned in the other sections of the file.  For example, the bantime (number of seconds to block an IP address) applies to the [sshd] section, as does the backend = systemd setting.  If we want Fail2ban to help protect WordPress, we will want Fail2ban to monitor a variety of log files, which cannot be done with the backend = systemd setting, so that setting will need to be modified in other sections for the file.  [sshd] describes the sshd jail, so we will need to select logical names for the sections of the file that will be added later.  The sshd jail was not defined (actually, not enabled – it is defined in another configuration file) when Fail2ban was first started, so we need to let Fail2ban know that it should load/reload the sshd jail configuration, and then verify that the jail is functional:

fail2ban-client reload sshd
fail2ban-client status sshd

If you wait a couple of minutes between executing the first of the above and second of the above commands, you may see output similar to this, which indicates that some candidates for blocking were identified and blocked, and a notification email was sent to the email address specified by the destemail setting:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 307
   |- Total banned:     307
   `- Banned IP list:   1.215.253.186 101.78.2.106 103.15.61.138 103.224.105.7 103.248.234.3 103.253.211.244 ...

Protecting WordPress running on Fedora 22 with Fail2ban.

When an attempt is made to access the password protected /wp-admin section of a WordPress site, and a bad password is entered, by default WordPress silently destroys that failed connection attempt, so Fail2ban is not able to help by blocking repeat offenders.  A partial solution that I found on several websites is to add the following code near the start of the WordPress theme’s functions.php file:

add_action('wp_login_failed', 'log_wp_login_fail'); // hook failed login
function log_wp_login_fail($username) {
        error_log("WP login failed for username: $username");

Once that code is in place, some of the bad login attempts will be written to either the /var/log/httpd/error_log or /var/log/httpd/ssl_error_log file.  You might then start seeing errors such as these buried in those files:

[Thu Aug 13 10:17:43.578391 2015] [auth_basic:error] [pid 30933] [client 75.145.nnn.nnn:50683] AH01618: user admin not found: /wp-admin/css/login.min.css, referer: http://www.websitehere.com/wp-login.php
[Thu Aug 13 19:12:53.054913 2015] [:error] [pid 2060] [client 50.62.136.183:33789] WP login failed for username: k-mm
[Thu Aug 13 20:13:02.316777 2015] [:error] [pid 1873] [client 50.62.136.183:42677] WP login failed for username: k-mm
[Thu Aug 13 21:13:12.012160 2015] [:error] [pid 15701] [client 50.62.136.183:52432] WP login failed for username: k-mm.com
[Thu Aug 13 21:28:32.073261 2015] [:error] [pid 15697] [client 50.62.136.183:58571] WP login failed for username: k-mm.com
[Thu Aug 13 21:58:43.118303 2015] [:error] [pid 21245] [client 50.62.136.183:52059] WP login failed for username: k-mm.com
[Thu Aug 13 22:03:49.150456 2015] [:error] [pid 21244] [client 50.62.136.183:60540] WP login failed for username: k-mm.com
[Thu Aug 13 22:23:28.348351 2015] [:error] [pid 15688] [client 50.62.136.183:52911] WP login failed for username: k-mm.com
[Thu Aug 13 23:14:14.453002 2015] [:error] [pid 19632] [client 50.62.136.183:37700] WP login failed for username: admin
[Fri Aug 14 01:14:15.455095 2015] [:error] [pid 5085] [client 50.62.136.183:45656] WP login failed for username: administrator
[Fri Aug 14 02:14:16.478660 2015] [:error] [pid 4114] [client 50.62.136.183:53068] WP login failed for username: administrator

In the above, note the behavior of the computer at IP address 50.62.136.183 – that computer is slowly hitting the server with different username and password combination – slow so as not to set off blocking utilities like Fail2ban that might be configured to start blocking when there have been, for instance, five bad password attempt in an hour.  Note that I stated that the addition to the theme’s functions.php file would help to identify some of the bad login attempts – to see the others, the /var/log/httpd/access_log and /var/log/httpd/ssl_access_log files must also be monitored.  In those files you may see patterns such as these where a single IP address will try to rapidly and repeatedly post to the /wp-login.php file for more than eight hours straight:

85.97.41.164 - - [12/Aug/2015:17:17:34 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:35 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:36 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:37 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:38 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:38 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:40 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:42 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:43 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
...
109.228.0.250 - - [13/Aug/2015:01:42:43 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:48 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:49 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:50 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:56 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:56 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"

Obviously, the computers at those IP addresses were up to no good, and should also be blocked.  Another interesting pattern that might be seen in the access_log or ssl_access_log files is an attacker trying to retrieve the login of the first author username in WordPress, working slowly to try logging into the website so as not to trip protection utilities like Fail2ban that identify multiple failed logins from the same IP address in a short period of time:

185.93.187.69 - - [20/Aug/2015:00:38:16 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:00:38:20 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:00:58:35 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:00:58:37 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:19:20 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:01:19:22 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:39:45 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:01:39:46 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:59:59 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:02:00:00 -0400] "GET /wp-login.php HTTP/1.1" 403 221

You might also see something like this in the access_log or ssl_access_log file:

220.163.10.250 - - [17/Aug/2015:21:03:43 -0400] "DELETE / HTTP/1.1" 400 226

I strongly suspect that the computer at IP address 220.163.10.250 had other uses in mind for my website.  From the documentation:

“The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location. “

A quick method to determine if a potential attacker tried to use the above DELETE request is to use the grep command to search within the ssl_access_log and access_log files:

grep "DELETE" /var/log/httpd/ssl_access_log*
grep "DELETE" /var/log/httpd/access_log*

Another set of attempted compromises that is not directed at WordPress sites are also visible in the ssl_access_log and access_log files:

162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php HTTP/1.1" 404 209 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php5 HTTP/1.1" 404 210 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php-cgi HTTP/1.1" 404 213 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php.cgi HTTP/1.1" 404 213 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php4 HTTP/1.1" 404 210 "-" "-"
195.145.157.189 - - [30/Jul/2015:12:07:38 -0400] "GET /cgi-bin/test-cgi HTTP/1.1" 404 214 "-" "the beast"
37.144.20.31 - - [01/Aug/2015:09:34:10 -0400] "GET /tmUnblock.cgi HTTP/1.1" 400 226 "-" "-"
69.64.46.86 - - [03/Aug/2015:01:48:28 -0400] "GET /cgi-bin/rtpd.cgi HTTP/1.0" 404 214 "-" "-"
69.64.46.86 - - [14/Aug/2015:01:24:35 -0400] "GET /cgi-bin/rtpd.cgi HTTP/1.0" 404 214 "-" "-"
23:46.148.18.122 - - [16/Aug/2015:20:30:17 -0400] "GET /tmUnblock.cgi HTTP/1.1" 403 - "-" "-"
23:46.148.18.122 - - [16/Aug/2015:20:30:17 -0400] "GET /hndUnblock.cgi HTTP/1.1" 403 - "-" "-"
88.202.224.162 - - [23/Aug/2015:07:05:15 -0400] "GET //cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%2 HTTP/1.1" 404 211
80.82.65.186 - - [01/Aug/2015:08:42:51 -0400] "GET //cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211
46.165.220.215 - - [16/Aug/2015:20:51:51 -0400] "GET /cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211
46.165.220.215 - - [17/Aug/2015:03:09:59 -0400] "GET /cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211

If any of the above appear to be interesting, you might try a Google search to see what the remote computers were attempting to compromise.

Far less obnoxious are entries that show your Nagios monitoring utility checking the website availability:

50.196.nnn.nnn - - [19/Aug/2015:09:30:54 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:07 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:42 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:47 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"

As well as random computers trying to download a file named wpad.dat (in the webserver root directory execute touch wpad.dat to create a zero byte file for that name – this is important if your client computers should not be trying to retrieve such a file and you have a custom error page configured for the website that is a feature rich web page).  There is a chance that your client computers could be searching for this file due to a specific configuration setting:

WebNotDatabaseWPAD

Example output, showing repeated requests, is shown below:

76.29.115.160 - - [20/Aug/2015:02:07:40 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:07:46 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:08:03 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:08:14 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"

Regular Expression Building Assistance:

If we intend to have Fail2ban help protect WordPress running on Apache on Fedora 22 Linux, we need to first create “filter” files that contain the regular expressions needed to recognize bad guy attempted access.  The filter files are located in the /etc/fail2ban/filter.d/ directory and all end with .conf, although the .conf portion of the filename is not specified in the /etc/fail2ban/jail.d/local.conf file that we created earlier.  I will create separate filter files for ssl and non-ssl log files, although that is not required.  The first filter file is apache-wp-login.conf:

vi /etc/fail2ban/filter.d/apache-wp-login.conf

I set that file to have four regular expressions to recognize a bad guy’s attempted access (one or two of the regular expressions below may be incorrect because I have not had enough recent practice at writing regular expressions):

[Definition]
failregex = [[]client <HOST>[]] WP login failed.*
            [[]client <HOST>[]] client denied.*wp-login.php
            .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
            .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
ignoreregex =

Save the file and exit vi.  Verification of the regular expression syntax is important.  The fail2ban-regex utility will process a Linux log file of your choice using one of the regular expression filters that you create in the /etc/fail2ban/filter.d/ directory.  For example, to test the filter than was created above, execute the following command:

fail2ban-regex --print-all-matched /var/log/httpd/error_log /etc/fail2ban/filter.d/apache-wp-login.conf

Your output may be similar to what appears below (note that I processed an error_log from a previous week:

Running tests
=============
 
Use   failregex filter file : apache-wp-login, basedir: /etc/fail2ban
Use         log file : /var/log/httpd/error_log-20150816
Use         encoding : UTF-8
 
 
Results
=======
 
Failregex: 40 total
|-  #) [# of hits] regular expression
|   3) [26] .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
|   4) [14] .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
`-
 
Ignoreregex: 0 total
 
Date template hits:
|- [# of hits] date format
|  [140] (?:DAY )?MON Day 24hour:Minute:Second(?:\.Microseconds)?(?: Year)?
`-
 
Lines: 144 lines, 0 ignored, 40 matched, 104 missed [processed in 0.24 sec]
|- Matched line(s):
...
|  [Thu Aug 13 22:03:49.150456 2015] [:error] [pid 21244] [client 50.62.136.183:60540] WP login failed for username: k-mm.com
|  [Thu Aug 13 22:23:28.348351 2015] [:error] [pid 15688] [client 50.62.136.183:52911] WP login failed for username: k-mm.com
|  [Thu Aug 13 23:14:14.453002 2015] [:error] [pid 19632] [client 50.62.136.183:37700] WP login failed for username: admin
|  [Fri Aug 14 01:14:15.455095 2015] [:error] [pid 5085] [client 50.62.136.183:45656] WP login failed for username: administrator
|  [Fri Aug 14 02:14:16.478660 2015] [:error] [pid 4114] [client 50.62.136.183:53068] WP login failed for username: administrator
|  [Fri Aug 14 13:02:10.181252 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:12.819515 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:14.880515 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:29.497034 2015] [:error] [pid 3357] [client 75.145.nnn.nnn:54798] WP login failed for username: k-mm, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:29.531482 2015] [auth_basic:error] [pid 3357] [client 75.145.nnn.nnn:54798] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
...

The /etc/fail2ban/filter.d/apache-wp-login-ssl.conf filter file that I created is identical to the /etc/fail2ban/filter.d/apache-wp-login.conf file:

[Definition]
failregex = [[]client <HOST>[]] WP login failed.*
            [[]client <HOST>[]] client denied.*wp-login.php
            .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
            .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
ignoreregex =

After saving the file and exiting vi, we are able to test the filter:

fail2ban-regex --print-all-matched /var/log/httpd/ssl_error_log /etc/fail2ban/filter.d/apache-wp-login-ssl.conf

The wordpress-login.conf and wordpress-login-ssl.conf filter files will be used to examine the /var/log/httpd/access_log and /var/log/httpd/ssl_access_log files, respectively.

The /etc/fail2ban/filter.d/wordpress-login.conf file (note once again that one or two of the regular expressions used for matching may need to be adjusted):

[Definition]
failregex = ^<HOST> .* "POST .*\/wp-login.php HTTP/1.0" 403 .*$
            ^<HOST> .* "POST .*\/wp-login.php HTTP/1.1" 403 .*$
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 403
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 200
            ^<HOST> .* "GET .*wp-login.php HTTP/1.*" 403 221
            ^<HOST> .* "GET ..author=1 HTTP/1.*" 302 -
ignoreregex =

The /etc/fail2ban/filter.d/wordpress-login-ssl.conf file:

[Definition]
failregex = ^<HOST> .* "POST .*\/wp-login.php HTTP/1.0" 403 .*$
            ^<HOST> .* "POST .*\/wp-login.php HTTP/1.1" 403 .*$
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 403
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 200
            ^<HOST> .* "GET .*wp-login.php HTTP/1.*" 403 221
            ^<HOST> .* "GET ..author=1 HTTP/1.*" 302 -
ignoreregex =

To test those two filters, use these commands:

fail2ban-regex --print-all-matched /var/log/httpd/access_log /etc/fail2ban/filter.d/wordpress-login.conf
fail2ban-regex --print-all-matched /var/log/httpd/ssl_access_log /etc/fail2ban/filter.d/wordpress-login-ssl.conf

Added August 31, 2015:

I have found that a couple of computers on the Internet are trying to access a variety of *.cgi files in rapid fashion, resulting in entries such as these being written to the /var/log/httpd/error_log file:

[Sun Aug 30 20:38:08.187093 2015] [cgi:error] [pid 6426] [client 64.15.155.177:53122] AH02811: script not found or unable to stat: /var/www/cgi-bin/webmap.cgi
[Sun Aug 30 20:38:08.271430 2015] [cgi:error] [pid 6230] [client 64.15.155.177:53316] AH02811: script not found or unable to stat: /var/www/cgi-bin/whois.cgi
[Sun Aug 30 20:38:08.599455 2015] [cgi:error] [pid 6094] [client 64.15.155.177:54035] AH02811: script not found or unable to stat: /var/www/cgi-bin/register.cgi
[Sun Aug 30 20:38:08.733852 2015] [cgi:error] [pid 6453] [client 64.15.155.177:54213] AH02811: script not found or unable to stat: /var/www/cgi-bin/download.cgi
[Sun Aug 30 20:38:09.048479 2015] [cgi:error] [pid 5353] [client 64.15.155.177:54516] AH02811: script not found or unable to stat: /var/www/cgi-bin/shop.cgi
[Sun Aug 30 20:38:09.533326 2015] [cgi:error] [pid 5673] [client 64.15.155.177:56107] AH02811: script not found or unable to stat: /var/www/cgi-bin/profile.cgi
[Sun Aug 30 20:38:09.736446 2015] [cgi:error] [pid 6455] [client 64.15.155.177:56274] AH02811: script not found or unable to stat: /var/www/cgi-bin/about_us.cgi
[Sun Aug 30 20:38:09.830315 2015] [cgi:error] [pid 6456] [client 64.15.155.177:56734] AH02811: script not found or unable to stat: /var/www/cgi-bin/php.fcgi
[Sun Aug 30 20:38:09.918823 2015] [cgi:error] [pid 4232] [client 64.15.155.177:56923] AH02811: script not found or unable to stat: /var/www/cgi-bin/calendar.cgi
[Sun Aug 30 20:38:10.013162 2015] [cgi:error] [pid 6423] [client 64.15.155.177:57115] AH02811: script not found or unable to stat: /var/www/cgi-bin/download.cgi
[Sun Aug 30 20:38:10.106597 2015] [cgi:error] [pid 6425] [client 64.15.155.177:57399] AH02811: script not found or unable to stat: /var/www/cgi-bin/light_board.cgi
[Sun Aug 30 20:38:10.193901 2015] [cgi:error] [pid 6426] [client 64.15.155.177:57574] AH02811: script not found or unable to stat: /var/www/cgi-bin/main.cgi
[Sun Aug 30 20:38:10.288724 2015] [cgi:error] [pid 6230] [client 64.15.155.177:57754] AH02811: script not found or unable to stat: /var/www/cgi-bin/search.cgi
[Sun Aug 30 20:38:10.516842 2015] [cgi:error] [pid 5349] [client 64.15.155.177:57949] AH02811: script not found or unable to stat: /var/www/cgi-bin/test.cgi
[Sun Aug 30 20:38:10.601953 2015] [cgi:error] [pid 6094] [client 64.15.155.177:58409] AH02811: script not found or unable to stat: /var/www/cgi-bin/file_up.cgi

If you have Fail2ban running on the webserver, and you are seeing entries like the above in the error_log file, consider creating a file named /etc/fail2ban/filter.d/apache-cgi-bin.conf with the following contents:

[Definition]
failregex   = ^.*\[cgi:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] AH02811: script not found or unable to stat: \/var\/www\/cgi-bin.*$
ignoreregex =

To test the above filter definition, execute this command:

fail2ban-regex --print-all-matched /var/log/httpd/error_log /etc/fail2ban/filter.d/apache-cgi-bin.conf

(Note that the steps that follow assume that the local.conf file has already been created, see the steps below.)  To set up the jail that uses the above filter, in the /etc/fail2ban/jail.d/local.conf file, you would then add the following lines, which will setup blocking when a search locates five or more matching entries from the same IP address within two days:

[apache-cgi-bin]
enabled  = true
filter   = apache-cgi-bin
logpath  = /var/log/httpd/error_log
bantime  = 2592000
findtime = 172800
port     = http,https
maxretry = 5
backend  = polling
journalmatch =

To activate the jail, execute:

fail2ban-client reload apache-cgi-bin

To see the jail status, execute:

fail2ban-client status apache-cgi-bin

Below is sample output for the above command:

Status for the jail: apache-cgi-bin
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     111
|  `- File list:        /var/log/httpd/error_log
`- Actions
   |- Currently banned: 4
   |- Total banned:     4
   `- Banned IP list:   118.219.233.133 27.254.67.157 118.163.223.214 64.15.155.177

Added September 14, 2015:

I noticed a couple of additional suspicious access entries in the access_log file.  The first set of entries appears to be from a computer looking for a wide range of web server vulnerabilities:

185.25.48.89 - - [13/Sep/2015:22:57:49 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:50 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:55 -0400] "POST /uploadify/uploadify.php HTTP/1.1" 301 - "http://k-mm.com/uploadify/uploadify.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:57:58 -0400] "GET /samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:59 -0400] "GET /samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:02 -0400] "POST /wp-admin/admin-ajax.php HTTP/1.1" 200 1 "http://k-mm.com/wp-admin/admin-ajax.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:06 -0400] "GET /wp-content/plugins/revslider/temp/update_extract/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:06 -0400] "GET /wp-content/plugins/revslider/temp/update_extract/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:09 -0400] "POST /php-ofc-library/ofc_upload_image.php?name=sample.php HTTP/1.1" 301 - "/php-ofc-library/ofc_upload_image.php?name=sample.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:12 -0400] "GET /tmp-upload-images/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:13 -0400] "GET /tmp-upload-images/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:13 -0400] "GET /large-machining-fabricating-capabilities/ HTTP/1.1" 200 50109 "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:17 -0400] "POST /components/com_creativecontactform/fileupload/index.php HTTP/1.1" 301 - "/components/com_creativecontactform/fileupload/index.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:20 -0400] "GET /components/com_creativecontactform/fileupload/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:21 -0400] "GET /components/com_creativecontactform/fileupload/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:27 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:28 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:31 -0400] "HEAD /plugins/editor.zoho/agent/save_zoho.php HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:32 -0400] "HEAD /sites/all/libraries/elfinder/elfinder.html HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:33 -0400] "POST /wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php HTTP/1.1" 200 1 "/wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
@
185.25.48.89 - - [13/Sep/2015:22:58:27 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:28 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:31 -0400] "HEAD /plugins/editor.zoho/agent/save_zoho.php HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:32 -0400] "HEAD /sites/all/libraries/elfinder/elfinder.html HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:33 -0400] "POST /wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php HTTP/1.1" 200 1 "/wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:34 -0400] "GET /wp-content/plugins/wpallimport/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:35 -0400] "GET /wp-content/plugins/wpallimport/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:38 -0400] "POST /server/php/ HTTP/1.1" 301 - "/server/php/" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:41 -0400] "GET /server/php/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:42 -0400] "GET /server/php/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"

The second set of entries appear to be from two different computers that are apparently trying to take advantage of a SQL injection attempt to deface a website, or something similar:

122.154.24.254 - - [14/Sep/2015:03:29:38 -0400] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.154.24.254 - - [14/Sep/2015:03:29:41 -0400] "GET /pma/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.154.24.254 - - [14/Sep/2015:03:29:45 -0400] "GET /myadmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:22 -0400] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:27 -0400] "GET /pma/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:33 -0400] "GET /myadmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"

While the hacking attempts were unsuccessful, I decided that there is little point in wasting the server’s resources with similar attempts.  I created a new Fail2ban filter with the filename /etc/fail2ban/filter.d/apache-block-misc-php.conf and added the following lines to recognize the above entries in the Apache access_log file:

[Definition]
failregex = ^<HOST> .* "POST .*uploadify.php HTTP.1.*" .*$
            ^<HOST> .* "HEAD .*uploadify.php HTTP.1.*" .*$
            ^<HOST> .* "POST .*ofc_upload_image.php.*" .*$
            ^<HOST> .* "POST .*fileupload.index.php .*" .*$
            ^<HOST> .* "HEAD .*save_zoho.php .*" .*$
            ^<HOST> .* "POST .*save_zoho.php .*" .*$
            ^<HOST> .* "HEAD .*elfinder.html .*" .*$
            ^<HOST> .* "POST .*elfinder.html .*" .*$
            ^<HOST> .* "GET .*scripts.setup.php .*" .*$
            ^<HOST> .* "POST .*scripts.setup.php .*" .*$
            ^<HOST> .* "GET .*\/samplc.php .*" .*$
            ^<HOST> .* "GET .*\/?author=.*" .*$
            ^<HOST> .* "GET .*abdullkarem.*" .*$
            ^<HOST> .* "GET .*\/uploadify.php.*" .*$
            ^<HOST> .* "GET .*\/bin\/perl .*$
            ^<HOST> .* "GET .*wp-admin\/admin-ajax.php .*" .*$
            ^<HOST> .* "GET <title>phpMyAdmin HTTP.*$
            ^<HOST> .* "GET \/phpmyadmin.*$
            ^<HOST> .* "GET \/phpMyAdmin.*$
            ^<HOST> .* "GET \/PMA\/.*$
            ^<HOST> .* "GET \/pma\/.*$
            ^<HOST> .* "GET \/admin\/.*$
            ^<HOST> .* "GET \/dbadmin\/.*$
            ^<HOST> .* "GET \/mysql\/.*$
            ^<HOST> .* "GET \/myadmin\/.*$
            ^<HOST> .* "GET \/sqlmanager\/.*$
            ^<HOST> .* "GET \/mysqlmanager\/.*$
            ^<HOST> .* "GET \/wcd\/top.xml.*$
            ^<HOST> .* "GET \/wcd\/system_device.xml.*$
            ^<HOST> .* "GET \/wcd\/system.xml.*$
            ^<HOST> .* "GET \/openurgencevaccin\/index.php.*$
            ^<HOST> .* "GET \/zeuscms\/index.php.*$
            ^<HOST> .* "GET \/phpcoin\/license.php.*$
            ^<HOST> .* "GET \/authadmin\/.*$
            ^<HOST> .* "GET \/backup\/.*$
            ^<HOST> .* "GET \/backups\/.*$
            ^<HOST> .* "GET \/bak\/.*$
            ^<HOST> .* "GET \/cbi-bin\/.*$
            ^<HOST> .* "GET \/ccard\/.*$
            ^<HOST> .* "GET \/ccards\/license.php.*$
            ^<HOST> .* "GET \/cd-cgi\/.*$
            ^<HOST> .* "GET \/cfide\/.*$
            ^<HOST> .* "GET \/cgi\/.*$
            ^<HOST> .* "POST .*\/fileupload\/index.php.*$
            ^<HOST> .* "POST .*\/php\/index.php.*$
            ^<HOST> .* "GET .*wp-config.php.*$
            ^<HOST> .* "POST .*\/examples\/upload.php.*$
ignoreregex =

Once the new filter file is created, test the filter to see if it allows Fail2ban to find any matching lines in the access_log:

fail2ban-regex --print-all-matched /var/log/httpd/access_log /etc/fail2ban/filter.d/apache-block-misc-php.conf

If it appears that the filter is finding matching lines, add a new jail definition in the /etc/fail2ban/jail.d/local.conf file (note that maxretry is set to 2):

[apache-block-misc-php]
enabled = true
filter   = apache-block-misc-php
logpath  = /var/log/httpd/access_log
bantime = 2592000
findtime = 86400
port    = http,https
maxretry = 2
backend = polling
journalmatch =

To activate the new jail, execute the reload command:

fail2ban-client reload apache-block-misc-php

To check the status of the new jail, execute the status command:

fail2ban-client status apache-block-misc-php

Sample output is shown below:

Status for the jail: apache-block-misc-php
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     30
|  `- File list:        /var/log/httpd/access_log
`- Actions
   |- Currently banned: 4
   |- Total banned:     4
   `- Banned IP list:   114.27.9.31 122.154.24.254 122.155.190.132 185.25.48.89

For Fail2ban to use the filters that were just created, we must add additional lines (jail descriptions) to the /etc/fail2ban/jail.d/local.conf file:

vi /etc/fail2ban/jail.d/local.conf

At the end of the file add the following four jail definitions (note that without the backend and journalmatch lines the jails will not work due to the settings in the [DEFAULT] section of this file):

[apache-wp-login]
enabled = true
filter   = apache-wp-login
logpath  = /var/log/httpd/error_log
bantime  = 2592000
findtime = 3600
port    = http,https
maxretry = 5
backend  = polling
journalmatch =
 
[apache-wp-login-ssl]
enabled = true
filter   = apache-wp-login-ssl
logpath  = /var/log/httpd/ssl_error_log
bantime  = 2592000
findtime = 3600
port    = http,https
maxretry = 5
backend  = polling
journalmatch =
  
[wordpress-login]
enabled = true
filter   = wordpress-login
logpath  = /var/log/httpd/access_log
bantime = 345600
findtime = 86400
port    = http,https
maxretry = 6
backend = polling
journalmatch =
 
[wordpress-login-ssl]
enabled = true
filter   = wordpress-login-ssl
logpath  = /var/log/httpd/ssl_access_log
bantime = 345600
findtime = 86400
port    = http,https
maxretry = 6
backend = polling
journalmatch =

Save the file and exit vi.  Next we need to instruct Fail2ban to recognize the four new jails:

fail2ban-client reload apache-wp-login
fail2ban-client reload apache-wp-login-ssl
fail2ban-client reload wordpress-login
fail2ban-client reload wordpress-login-ssl

As an alternative to the above, we could just restart Fail2ban, which will restart all of the jails, and potentially spam your inbox with ssh blocking notifications:

systemctl restart fail2ban.service

Checking the status of the jails is quite simple to accomplish:

fail2ban-client status apache-wp-login
fail2ban-client status apache-wp-login-ssl
fail2ban-client status wordpress-login
fail2ban-client status wordpress-login-ssl

You might be curious about the emails that Fail2ban sends.  Below is a portion of an actual email that I received from Fail2ban recently:

Hi,

The IP 46.119.117.47 has just been banned by Fail2Ban after
12 attempts against wordpress-login.

Here is more information about 46.119.117.47:

[Querying whois.ripe.net]
[whois.ripe.net]
% This is the RIPE Database query service.
% The objects are in RPSL format.
%
% The RIPE Database is subject to Terms and Conditions.
% See http://www.ripe.net/db/support/db-terms-conditions.pdf

% Note: this output has been filtered.
%       To receive output for a database update, use the “-B” flag.

% Information related to ‘46.118.0.0 – 46.119.255.255’

% Abuse contact for ‘46.118.0.0 – 46.119.255.255’ is ‘abuse@kyivstar.net’

inetnum:        46.118.0.0 – 46.119.255.255
descr:          Golden Telecom LLC
netname:        UA-SVITONLINE-20100517
org:            ORG-SOGT1-RIPE
country:        UA
admin-c:        GTUA-RIPE
tech-c:         GTUA-RIPE
status:         ALLOCATED PA
mnt-by:         RIPE-NCC-HM-MNT
mnt-lower:      GTUA-MNT
mnt-lower:      GTUA-WO-MNT
mnt-domains:    GTUA-ZONE-MNT
mnt-domains:    GTUA-MNT
mnt-routes:     GTUA-RT-MNT
mnt-routes:     GTUA-MNT
created:        2010-05-17T08:47:45Z
last-modified:  2011-08-04T15:58:57Z
source:         RIPE # Filtered

organisation:   ORG-SOGT1-RIPE
org-name:       Golden Telecom LLC
org-type:       LIR
address:        15/15/6 V. Khvojki str.
address:        04080
address:        Kiev
address:        UKRAINE
phone:          +380444900000
fax-no:         +380444900048
admin-c:        AEL17-RIPE
admin-c:        NP1533-RIPE
mnt-ref:        RIPE-NCC-HM-MNT
mnt-ref:        GTUA-MNT
mnt-by:         RIPE-NCC-HM-MNT
abuse-c:        GTL6-RIPE
created:        2004-04-17T12:09:58Z
last-modified:  2015-07-17T13:48:48Z
source:         RIPE # Filtered

role:           Golden Telecom Ukraine NOC
address:        Golden Telecom
address:        4 Lepse blvr
address:        Kiev, 03067, Ukraine
phone:          +380 44 4900000
fax-no:         +380 44 4900048
remarks:        All abuse notifications have to be sent on:
abuse-mailbox:  abuse@kyivstar.net
admin-c:        AEL17-RIPE
admin-c:        NP1533-RIPE
nic-hdl:        GTUA-RIPE
mnt-by:         GTUA-MNT
created:        2007-07-25T09:02:04Z
last-modified:  2014-06-17T08:24:26Z
source:         RIPE # Filtered

% Information related to ‘46.119.112.0/20AS15895’

route:          46.119.112.0/20
descr:          Kyivstar GSM, Kiev, Ukraine
origin:         AS15895
mnt-by:         GTUA-MNT
created:        2012-03-21T09:29:14Z
last-modified:  2012-03-21T09:29:14Z
source:         RIPE # Filtered

% This query was served by the RIPE Database Query Service version 1.80.1 (DB-2)
Lines containing IP:46.119.117.47 in /var/log/httpd/access_log

I am not sure why, but this particular email did not list the lines from the access_log that matched the filter rule.

Protecting WordPress running on Fedora 22 with .htaccess Files

One step that you may want to take is to password protect the /wp-admin directory on your web server.  To do that, you would create a new Linux user with a username and password that are difficult to guess based on your website name and WordPress users – the password should be at least eight characters long with upper and lower case letters, numbers, and punctuation marks.  Then, using tips from the last post in this message thread, create a file name .htaccess in the /wp-admin directory.  Inside that file, add the following lines (replace /full/path/to/your/wp-admin with the directory where you will later create a .htpasswd file):

AuthName "Admin Area"
AuthType Basic
AuthUserFile /full/path/to/your/wp-admin/.htpasswd
require valid-user
 
<Files admin-ajax.php>
    Order allow,deny
    Allow from all
    Satisfy any
</Files>

Next use the htpasswd generator website to create an encrypted version of the password for the Linux username.  For example, if you created the Linux user hillbillyforpresident with a password of GreatScott1TrumpIsAhead? the htpasswd website would instruct you to create a .htpasswd file with the following contents:

hillbillyforpresident:$apr1$gAgbX0SU$YjtXg5pAvXrD6i.F2lh6z1

Make certain that the .htaccess file (and possibly the .htpasswd file also) have read/write access for the owner, read access for the group in which Apache runs (the Apache user should not own the files), and that the files are not world readable.  For example:

chmod 640 /var/www/html/wp-admin/.htaccess

The wp-config.php file should also be protected with similar file permissions:

chmod 640 /var/www/html/wp-config.php

The .htaccess file in the web server’s root directory should also be adjusted to control which files may be accessed.  Below the # END WordPress line in the file, consider adding the following (once you understand what the lines accomplish – note that the entry containing 123\.123\.123\.123 should allow the IP address 123.123.123.123 to access the wp-login.php file):

# Block access to files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
 
RewriteCond %{REQUEST_URI} ^(.*)?wp-login\.php(.*)$ [OR]
RewriteCond %{REQUEST_URI} ^(.*)?wp-admin$
RewriteCond %{REMOTE_ADDR} !^123\.123\.123\.123$
RewriteRule ^(.*)$ - [R=403,L]
</IfModule>
 
<files wp-config.php>
order allow,deny
deny from all
</files>
 
<Files .htaccess>
 order allow,deny
 deny from all
</Files>
 
# Stop Apache from serving .ht* files
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
 
Options -Indexes

WordPress and SELinux – a Headache Waiting to Attack

From what I understand, everything in the webserver’s root directory is set by default to the httpd_sys_content_t SELinux context – and sometimes that context is not present when files are copied into various subdirectories that are accessible to Apache.  The following command resets the SELinux context to the default context:

chcon -R -v -t httpd_sys_content_t /var/www/

Using FTP integrated with WordPress to install updated plugins or new WordPress versions is a bit of a nightmare because different SELinux contexts are required for the different directories – I never did find a combination that worked.  As a result, I added the following line to the wp-config.php file so that FTP integration is not necessary:

define( 'FS_METHOD', 'direct');

Of course the WordPress upload directory must have the httpd_sys_rw_content_t SELinux context, so at some point the following command would need to be executed:

chcon -R -v -t httpd_sys_rw_content_t /var/www/html/wp-content/uploads/

The same command may also need to be executed for the WordPress plugins and upgrade directories (and probably a tempfiles directory) so that it is possible to install and update plugins using the WordPress interface.  Right now I do not permit WordPress to auto-update when a new version is released (this is due to the file system permissions that I use that only allow the apache user to read the files, not change the files).  I previously created a download directory in the /var directory.  Whenever I need to upgrade WordPress to a new version I use a script with the following contents (note that the script was pieced together based on what the WordPress release notes stated needed to be updated):

cd /var/downloads
rm -rf /var/downloads/wordpress
rm /var/downloads/wordpress.tar.gz
wget https://wordpress.org/latest.tar.gz
mv latest.tar.gz wordpress.tar.gz
tar -xzf wordpress.tar.gz
chcon -R -v -t httpd_sys_content_t /var/downloads/wordpress/
chown -R FileOwnerHere:ApacheGroupHere /var/downloads/wordpress/
find /var/downloads/wordpress/ -type d -exec chmod 2755 {} +
find /var/downloads/wordpress/ -type f -exec chmod 2644 {} +
cp -av /var/downloads/wordpress/wp-admin/* /var/www/html/wp-admin/
cp -av /var/downloads/wordpress/wp-includes/* /var/www/html/wp-includes/
cp -v /var/downloads/wordpress/wp-content/* /var/www/html/wp-content/
cp /var/downloads/wordpress/*.php /var/www/html/
cp /var/downloads/wordpress/*.txt /var/www/html/
cp /var/downloads/wordpress/*.html /var/www/html/

The above information is consolidated from weeks, maybe months, of hammering on a seemingly simple problem – 12 years later (OK, maybe 16 years later) and I am still in search of the Linux program named Setup.exe that configures everything that needs to be configured to get a job done quickly.  Oh, going out on a limb here, let’s ask for a GUI interface too that works with Putty.  Or, even further out on a limb, let’s ask for consistency of file paths, filenames, and commands across the 790+ Linux distributions and versions within each distribution so that a how-to article created two years ago is still valid today.  Stepping off the soap box… or SOAP box.

If any readers have comments or suggestions that improve upon the above information (or gently correct), please feel free to add a comment below.  Maybe someone else will find some of the above information useful to avoid putting a couple of extra dents in the top surface of their desk.





On the Topic of Technology… 8 – First 48 hours with Windows 10

3 08 2015

August 3, 2015 (Updated August 5, 2015, August 10, 2015, August 29, 2015)

(Back to the Previous Post in the Series)

I have been testing Windows 10 under the Windows Insider Program for several months using an old Dell Precision Core 2 Extreme computer (with NVidia graphics card) that was retired from engineering service a couple of years ago. I had some issues upgrading from one preview version to the next, such as the new Start menu failing to display – forcing a format and reinstall of the operating system. One version that automatically installed over the top of build 10074 caused the computer to continuously reboot. Odd, I thought, that a Windows update could cause that problem. I traced that issue back to disabling data execution protection (DEP) several years earlier on the engineering computer to keep some of the software from spontaneously crashing – turning DEP back on in the BIOS immediately resolved the reboot loop. I was still seeing occasional Start menu display glitches a week or so before the official release date for Windows 10 (July 29, 2015), but those problems were diminished with the application of Windows updates as the official release date neared.

—–

Quick note on August 12, 2015: I came across an article on Spiceworks that lead me down an interesting path since I am fighting driver issues on a Sony Vaio laptop.  In short, if you have a Sony computer, don’t expect Windows 10 compatible drivers for a while.  How long?  October or November 2015.

Windows10SonyWarning

In a related article, the following statement appears, apparently quoting an unconfirmed Sony source:

“In a message to Sony laptop owners, the firm has pleaded for patience when it comes to installing the new operating system (OS) due to the real risk of software or driver corruption that could result in a catastrophic data loss.

Sony’s advice goes on to say that if customers don’t wait for the test results then there is a chance that their computer “may no longer work as intended.” This can mean anything from the system crashing, becoming unresponsive or suffering from hardware damage. The bottom line is that tapping Sony to service your PC following any of these issues could result in “losing all of the data” on your computer.”

—–

A couple of years ago I bought a Sony SVE14AE13L (SVE14A27CXH) touch screen laptop as an incentive to learn how to use and troubleshoot Windows 8. With a Core i7 processor and 8GB of memory, I anticipated having an easy time working through Windows 8, and after watching several hours of how-to videos I was reasonably comfortable with Windows 8 – for someone who has worked with computers since the early 1980s it was a bit of a struggle, but not as bad as some people claimed (I also thought that Windows Vista was reasonably good before the release of Windows 7). That said, this computer was updated to Windows 8.1 on the day that the new version became available. This Sony laptop computer, however, had a couple of annoying behaviors. It would randomly wake up in the middle of the night, the touchpad would occasionally stop working, Internet Explorer on the computer was occasionally slow, and the touchpad would send random zoom in/out messages to the current application.

Windows 8.1 worked reasonably well on the Sony laptop, but I went ahead and reserved a copy of Windows 10 for the computer to get some experience with the release version of Windows 10. The first 48 hours of Windows 10 began this past Saturday. I grew tired of waiting for Microsoft to tell me that my copy of Windows 10 was available for installation, so I opened a Windows command line and executed this command: wuauclt.exe /updatenow – that seemed to kick start the upgrade, but it failed to install once roughly 3.5GB of installer files were downloaded. Another search of the Internet indicated that I should have first deleted everything in the C:\Windows\SoftwareDistribution\Download folder. After downloading another 3.5GB copy of the Windows 10 installer, I found that this attempt also failed after I clicked the Continue (or whatever that button showed) button in the Windows Update window. I started wondering what I was doing wrong, so I downloaded the ISO DVD image for Windows 10 from Microsoft.  Progress – writing the ISO DVD image to a DVD permitted the Windows 10 installer to start with a simple double-click.

Five or six hours in now, roughly 10.5GB downloaded from the Internet (I sure am happy that I no longer have the Internet connection with the 6GB monthly limit), and the Windows 10 installer is showing a message “Your PC will restart several times. Sit back and relax.” Don’t worry, be happy (click the picture for a larger view).

Windows10-95PercentComputerMayRestart

The install completed… with an error message, “The installation failed in the FIRST_BOOT phase with an error during MIGRATE_DATA operation.” Fantastic, at least the installer brought back the previous Windows 8.1 (click the picture for a larger view).

Windows10CouldNotInstallErrorFailedFirstBootMigrateData

That sure is an obscure error message. Google searches seemed to indicate that the problem could be caused by a corrupt profile, or a half dozen other issues.

I made another attempt at the upgrade, this time telling the installer NOT to install any Windows updates during the initial install. Perhaps eight or nine hours in, success – the Windows 10 Pro logon window is a keyboard tap away (note that the picture below was taken the following day).

Windows10FirstLogin

Great, Windows updates are automatically downloading and installing. Five minutes later, the laptop is apparently unhappy with Windows 10, a frown on a blue screen of death with the message “If you’d like to know more, you can search online later for this error: INTERNAL_POWER_ERROR

Windows10BlueScreenInternalPowerError

E-Moe-Gee! (emoji). Sure, collect your error info and restart. Recalling the message from the earlier picture: “Your PC will restart several times. Sit back and relax.” And thus started an automatic reboot loop, sometimes lasting just long enough to log into Windows 10 and display the Device Manager. In this case, that INTERNAL_POWER_ERROR message means that Windows 10 does not like the AMD Radeon HD 7600M series graphics card. Congratulations Microsoft and AMD, I have not had this level of difficulty with video card drivers since 1999 when I set up a triple boot on a computer with the latest NVidia graphics card, booting into Windows 98 (drivers easy to find), Windows NT 4.0 (a little more challenging), and Red Hat Linux with the Gnome X-Window desktop (on par with Windows 10 on this Sony laptop).

A couple of tips at this point. Pressing F8 during the initial Windows boot apparently does not display the old Windows 95 style boot menu that allows the computer to start in Safe Mode with limited functionality. If the computer is able to boot to the logon screen, there is a power button at the bottom-right of that screen. Hold down the Shift key on the keyboard, click the power button, and select Restart – if you continue to hold down the Shift key, you will have the option to Troubleshoot the computer. If you select Troubleshoot, then Advanced options, then Startup Settings, you will be able to select to start the computer in Safe Mode. Shutting off the computer when it is booting into Windows will often result in the next boot attempt taking the computer to the screen that allows selecting Troubleshoot, and eventually the option to start in Safe Mode. Once in Windows 10, holding the Windows flag key and pressing the X key on the keyboard has the same effect as right-clicking the Start button. A menu will appear permitting quick access to the Device Manager, Computer Management, Task Manager, and a variety of other computer administration tasks.

One of the tips that I found online for dealing with the INTERNAL_POWER_ERROR message was to quickly navigate to the Device Manager, and delete the Windows installed AMD Radeon HD 7600M item under the Display adapters heading. I tried that without success, and even tried deleting the Intel HD Graphics 4000 item under the Display adapters heading without success. Instructing Windows to scan for new devices resulted in two “Microsoft Basic Display Adapter” items being added under the Display adapters heading, leading to another blue screen after a couple of minutes.

Windows10DeviceManager1

Booting the computer to Safe Mode with Networking to do some troubleshooting, Windows 10 was stable. A web search using a different computer suggested that I needed to download the latest video card drivers for the AMD video card to fix the blue screen. I found that the new Edge web browser, which is the new default web browser on Windows 10, cannot start in Safe Mode, resulting in a message stating “This app can’t open. Microsoft Edge can’t be opened using the Built-in Administrator account. Sign in with a different account and try again.”  I was logged into the computer using my personal account, not the Built-In Administrator account, but I guess that detail did not matter.

Windows10CannotStartEdgeInSafeMode

Where did Microsoft hide Internet Explorer on Windows 10? Hold down the Windows key and press R. Type iexplore.exe and press the Enter key. Now, if only the wireless network worked in Safe Mode with Network Support! Trying again with the laptop connected by Cat 5e cable to a network switch, I managed to download the correct drivers. Sorry, cannot install AMD video card drivers in Safe Mode, “Failed to load detection driver.” Clicking OK caused the installer to hang at 100% complete. The previously registered Microsoft Word 2010 claimed that it could not verify that I was running an authentic version of Word 2010 while in Safe Mode, so the problem is not just with AMD when in Safe Mode.

Windows10CannotInstallATIDriversInSafeMode

Now what? Tell the computer to try reinstalling Windows 10 without installing updates – tried that, only to be greeted with a near instantaneous frowny face (a blue screen of death) upon completion. It took roughly 10 attempts to make it into the BIOS setup on the laptop (F2 did not work at the start of the boot) – I eventually found that applying about 200 pounds of pressure to the pink Assist button while powering on the Sony laptop allowed access to the BIOS setup. I found an option titled “Discrete Graphics Adapter” that was set to Enabled, so I changed that to Disabled, saved the changes, and managed to log into a stable copy of Windows 10… about 30 to 36 hours after the first download of Windows 10 initiated.

Windows10BIOSDisableDiscrete

After verifying that the computer worked fine with the Discrete Graphics Adapter disabled, I re-enabled that setting and made another attempt with the AMD graphics card.  The AMD graphics drivers installed without issue in Windows 10, but again resulted in a continuous reboot blue screen loop. I found that by quickly navigating to Device Manager after logon, I was able to set the AMD graphics device to Disabled before the computer would blue screen – once again Windows 10 was stable (and Microsoft Word 2010 worked OK too). Windows 10 installed various Windows updates, but simply refused to install the video driver for the Intel HD Graphics 4000 device.

Windows10WindowsUpdateCannotInstallIntelDrivers

As a result, Device Manager still shows a “Microsoft Basic Display Adapter” in Device Manager. Instructing Device Manager to update the drivers also failed.

Windows10DeviceManagerCannotInstallVideo

Even though the correct video card drivers are not installed, Windows 10 seems to work OK. Unfortunately, the problem where the touchpad would send random zoom in/out messages is still present – note the size of the zoomed icons in the desktop background in the following picture. I have yet to find a logical way to send the zoom back to normal on the desktop. The right side of the below picture shows the notification Action Center that is displayed by clicking the icon near the clock at the bottom-right of the screen.

Windows10VideoPlaybackNotifications2

The following picture shows that the Windows 8 new style apps are now able to appear on the desktop with regular Windows applications. I was shocked to see that Windows 8 would not permit overlapped windows for the new style apps – I think that the original release of Windows had the same problem, even though the Commodore Amiga from the same time period (mid to late 1980s) supported windows arranged on top of other windows. Cortana is shown at the left of the window in the picture below – apparently Cortana lost an argument with the Internet shortly before this screen capture was saved (Cortana is a bit evasive when asked “who are your programmers”). The Windows 10 scientific calculator appears at top-center, Microsoft Edge at bottom-center, Paint.Net at top-right, and the Microsoft Solitaire Collection app at the bottom right.

Windows10NewOldAppsNoInternet

As I stated above, overlapping Windows 8 apps are supported, and Cortana sent me to a web page about Easter Eggs – I thought that maybe that would be a good way to learn more about Cortana’s programmers. The speech input recognition seems to work very well in Cortana – as long as the Internet connection is not down.

Windows10OverlappingWindows

The picture below shows the new Start menu at the left – it is possible to stretch or narrow the Start menu to show more or less of the Windows 8 style live tiles on the Start menu. Changing settings is still a little confusing – for some settings it is necessary to click All Settings in the Action Center (the gray background window at the top-right), and in other cases it is necessary to use the Windows Vista style Control Panel (the white background window at the bottom-right).

Windows10StartMenuControlPanelAllSettings

The task view trick that was introduced with Windows Vista (Windows key and Tab key) still works, but has changed for the better. The Task View may also be opened by clicking the Task View button on the task bar at the bottom of the screen.

Windows10TaskView

The location of important settings seems to change with just about every new release of Windows. It would be nice if (almost) all settings could be found within a single interface. A trick that worked with older versions of Windows also works with Windows 10. Right-click the desktop (or inside any folder) and select New – Folder. Give that folder the following name:

GodMode.{ED7BA470-8E54-465E-825C-99712043E01C}

Double-clicking that folder to open it gives you just what you need (until the touchpad starts randomly sending zoom in/out messages).

Windows10GodMode1
Windows10GodMode2
Windows10GodMode3
Windows10GodMode4

Oh, it appears that the Oracle Database 11.2.0.3 database instance survived all of the upgrading nonsense, although it did take an exceptionally long time to start SQL Plus.

I have been working with computers for a long time – started back in the early 1980s. My new boss (almost five months now) at work for some reason thinks that I am a programmer… just a programmer? Last week I casually mentioned to him that I had read 30+ books about Oracle Database, and hinted that I co-authored another. I wonder if he has a clue about the books that I read about network administration (including a couple of CNE books in the late 1990s), operating systems, Microsoft Exchange (two for Exchange 2013, and other for previous versions), computer hardware troubleshooting, and a variety of other computer related skills. Well, at least he thinks that I am a programmer, and not just one who fixes computers (not that there is anything wrong with fixing computers, right Microsoft/Sony/AMD?).

As a suggestion, before you attempt a Windows 10 upgrade on a computer with an AMD video card, ask yourself if you have 24+ hours to spare and how much you like seeing frowns on blue screens. If the above is not enough of a warning, consider the before and after webcam photos captured by the Sony laptop’s webcam that follow.

Before picture: staring down Windows 8.1, trying to figure out why the window zoom keeps randomly changing:

Windows10Before

After Windows 10 is installed picture: note that more than the background changed.  I think that Cortana is stalking me now (why do I have a purple square target on my head?).  Just what is she doing in the middle of the night when the computer spontaneously turns on from a deep sleep?:

Windows10After

Humor aside, like Windows NT 4.0, once you get through the blue screens of death during the driver installs, Windows 10 will likely be rock solid stable.  I am already liking it more than Windows 8.1, even if it is not as fast on the same hardware.

Update August 4, 2015:

Below is a possible fix for the random zoom in/zoom out problem if your computer has a Synaptics touchpad.  This solution will probably work on Windows 8/8.1 also.  Near the bottom right of the screen, click the up pointing arrow to show all of the tray program icons.  There may be a tray icon named Synaptics Pointing Device that looks like a rectangle with two smaller rectangles below – click that tray icon, and then click Pointing Device Properties from the menu.  Note that if you also want to turn off the potentially annoying left-clicks due to touching the touchpad with a bit too much force, click the Tap to Click menu item to remove the checkmark in front of that item.

aWindows10FixUnintentionalZoom1

On the Device Settings tab, click the Settings button.  Remove the checkmark in front of Pinch Zoom, click the OK button, and then click the OK button in the Mouse Properties window.  This random zoom behavior has plagued the laptop for some time, but only became beyond bearable when the randomly activating feature affected the desktop icons in Windows 10.

Windows10FixUnintentionalZoom2

Update August 5, 2015:

If your desktop icons are super-sized as shown below (icons are shown at actual size) due to random zoom-in/zoom-out messages from your touchpad, there is a simple quick-fix (after shutting off this feature using the instructions above).  Right-click an open area of the desktop, select View and then Medium icons.

Windows10FixOverZoomedDesktopIcons

Microsoft released the first cumulative update KB3081424 for Windows 10 64 bit computers within the last hour – that update is being rolled out and installed automatically on all Windows 10 64 bit computers (Windows 10 computers that are joined to a domain may or may not automatically install this update).  After the update installed, I told the computer to go ahead and reboot.  After rebooting the computer, the Windows Start menu would not appear, Cortana would not respond, and the Action Center would not displayI saw this same behavior when testing the automatic updates in the various Technical Preview versions – in those cases I had to format the hard drive and reinstall Windows to recover.  Fortunately, in this case the functionality was restored by simply rebooting the computer (if this problem happens to you, there is no obvious way to tell Windows to reboot – just hold down Ctrl, then Alt, and press the Delete key once – a power option should appear near the bottom right of the screen to permit you to restart the computer).  This Start menu, Cortana, and Action Center functionality loss, if permanent and widespread, could cause a bit of havoc.

Windows10UpdateKillsStartMenu

Note that I am still fighting the Intel and AMD video card driver issues (Intel driver will not install, AMD driver installs with a blue screen).  I might have found a solution for the computer waking up unexpectedly in the middle of the night, but I will wait to post what I found until I confirm that the fix works.

Update 2 August 5, 2015:

Fixing the issue where the Intel HD Graphics 4000 device is listed as “Microsoft Basic Display Adapter” in Device Manager and Windows returns an error when installing the correct driver through Windows Update and Device Manager is a bit of a hassle.  The actual error message is not displayed when the driver fails to install.  If you experience this problem, immediately navigate to the C:\Windows\SoftwareDistribution\Download\Install folder, and locate a file that ends with .inf.  Right-click that file and select Install.  If Windows returns an error “There is no driver selected for the device information set or element.” that likely means that the company that set up the driver’s .inf file made an error in the file.

Windows10WindowsUpdateCannotInstallIntelDrivers2

The short-term solution for this problem is to download the Windows 7, 8, and 8.1 graphics driver package from the Intel website.  After a bit of searching, I found a suitable driver for the Intel HD Graphics 4000 device here.  After the installer completed, the computer had to restart – Device Manager then showed the HD Graphics 4000 device in place of the “Microsoft Basic Display Adapter” in Device Manager.  After the driver installation Windows Update still shows that the Windows 10 driver for the Intel graphics device fails to install, so there is still an issue that Microsoft/Intel need to rectify with the driver update.

Windows10FixIntel4000Driver

Now to tackle the blue screen of death caused by the AMD Radeon video driver…

Update August 10, 2015:

Peer-to-peer Windows Updates – potentially OK if the computer is connected to a trusted network, and the default Windows Update settings are adjusted; potentially dangerous at the default setting.  The default settings for Windows Updates essentially make your computer part of a world-wide Windows Update torrent.   If your computer is connected to a pay-as-you-go Internet provider, or if your Internet provider charges substantial fees for exceeding a certain monthly data cap (6GB per month, for instance), you should definitely modify the default update settings.  If you are concerned about your computer retrieving and automatically installing Windows updates from computers that have no association with Microsoft, other than the computers are running Windows 10, you should definitely modify the default update settings.  I was made aware of this particular issue when discussing Windows 10 with a relative who is an IT expert.  I also read a couple of articles today that described the torrent-like behavior of Windows 10 updates.

To fix the default setting, select Settings from the Start menu, then click Update & security.  Click Advanced options, then click Choose how updates are delivered.

Windows10DisableWindowsUpdateSharing1\

If your computer is always connected to a trusted network (never taken to a coffee shop, hotel, LAN party, etc.), consider changing the default setting to PCs on my local network – this setting could be beneficial if you have a pay-as-you-go or monthly capped Internet connection.  If you ever connect the computer to an untrusted network, consider changing the default On setting to Off.

Windows10DisableWindowsUpdateSharing2

Update August 29, 2015:

On August 5, 2015 I stated, ” I might have found a solution for the computer waking up unexpectedly in the middle of the night, but I will wait to post what I found until I confirm that the fix works.”  The fix that I implemented seems to have corrected this problem that has plagued the laptop for quite some time (possibly since new, nearly three years ago).  If your computer wakes up unexpectedly in the middle of the night, and you would like to end that behavior, click the Windows 10 Start Menu, then click Control Panel.  In the search box at the right type power and then click Edit power plan at the left.

Windows10DisableWakeUp1

In blue lettering near the top of the window you should see the words “Change settings for the plan: ” followed by the selected power plan name (High performance in the picture below) – make note of the selected power plan name.  Click Change advanced power settings.

Windows10DisableWakeUp2

Check to make certain that the same power plan appears selected in the list – if not, select the power plan that appeared in the previous window.  Click the + next to Sleep, then the + next to Allow wake timers.  Change both the On battery and Plugged in settings to Disable.  Finally, click OK to save the changed settings.  Note: it may be necessary to click the Change settings that are currently disabled link prior to making these changes.

Windows10DisableWakeUp3

I have now upgraded a computer from Windows 8.1 Pro (Ultimate?) to Windows 10 Pro (Sony laptop), another from Windows 8.1 Home to Windows 10 Home, another from Windows 7 Ultimate to Windows 10 Pro, and a fourth (and fifth) from Windows 7 Home to Windows 10 Home.  The Sony laptop has at least twice had a panic attack a couple of minutes after coming out of sleep.  During the panic attack the fan ramps up to maximum speed, the touch pad and keyboard stop responding, and the power button must be held in for 10 second to shut the computer off (the laptop does not go to sleep with a quick press of the power button).  The Sony computer has also crashed a couple of times with an irql_not_less_or_equal blue screen at least once in the last week.  The computer that was upgraded from Windows 7 Pro to Windows 10 Pro has spontaneously rebooted three times (twice within 30 minutes) while building Windows large icon thumbnails for a number of Apple Quicktime .mov video files.  The computer is connected to a true sign wave UPS, so I know that the reboot is not caused by a power problem (I was next to the computer each time the reboot happened, no blue screen, no notification entries in the Windows event logs except that the previous shut down was unexpected).  One of the computers upgraded from Windows 7 Home to Windows 10 Home is a Dell laptop where the G and H keys did not work correctly before or after the upgrade (some people have reported that this is a known intermittent problem with this particular Dell laptop model) – BIOS upgrades for the laptop refused to install following the upgrade to Windows 10 Home.  I do not yet have a status update from the other two upgraded computers.





Install Nagios on a Synology DiskStation DS415+ (Plex Support Also Added)

24 12 2014

December 24, 2014 (Modified December 29, 2014 – Added Plex Support Section, January 11, 2015 – Added Nagios Web Status and Fixed Status Logging, April 5, 2015 – Added Plex/Nagios/Wordpress Stress Test, May 25, 2015 – current Plex download supports CPU in DS415+)

(Back to the Previous Post in the Series)

This article describes how to compile and run Nagios 4.0.8 (with 2.0.3 plugins) on a Synology DiskStation DS415+ (64 bit), which utilizes an Intel Atom Avoton processor (cat /proc/cpuinfo indicates that the unit is using a 2.40GHz Atom C2538, and utilizes the DSM 5.1-5021 (the latest version as of December 24, 2014) operating system.  Nagios is a very useful network monitoring (and even Oracle Database monitoring with plugins) utility that uses email or other communication means to report device or service outages.  Not all Synology DiskStation NAS devices use Intel based CPUs – some of the less expensive DiskStations use ARM type processors (see this link to determine the type of CPU installed in a specific DiskStation).  It may be possible to produce a working version of Nagios on NAS devices that do not have Intel 32 bit or 64 bit processors (such as the DS212+), but I have not yet fully tested the procedure with many different NAS units.  Note that the even though the DS415+ and DS1813+ both use Intel CPUs running a 64 bit DSM version, the installation steps are a bit different (I tried various unsuccessful approaches over the last two months, and finally found a working combination).

Warning: A lot of what follows is based on experimentation, with the end goal of having Nagios running on a Synology DiskStation having the ability to ping devices on the network or the Internet, with an email sent to an administrator when a device stops responding to ping requests, and to send a second email when the device resumes responding to ping requests.  This functionality represents a small fraction of Nagios’ capabilities through the use of plugins (see my article that describes setting up the Oracle client on a Diskstation to permit Oracle Database monitoring).  File paths vary from one Linux distribution to the next, so that adds a bit of challenge to make certain that the files are placed in the required directory.  Copying a file to the wrong directory may temporarily disable the DiskStation and require the reinstallation of the Synology DSM operating system.  The directions below are not final, and quite likely do not represent the most efficient approaches to accomplish the end goal (note that the steps are more efficient than those for the DS1813+ and DS412+) – but the directions will hopefully be “close enough to correct” to allow the average reader of this blog to ping and send email alerts from a DiskStation.

I have relied on the free Nagios network monitoring solution since 2002 to provide an early warning of problems associated with network attached equipment including servers, production floor computers, switches, printers, wireless access points, IP cameras, website accessibility, Internet connection stability, etc.  While I rely on Nagios’ alerting system, I am not an expert at configuring the Nagios network monitoring system; the Nagios configuration documentation may be downloaded here.

First, make certain that the Telnet Service (or SSH Service if that is preferred) is enabled on the DiskStation.  In the DiskStation’s Control Panel, click Terminal & SNMP.

nagiosds415_enable telnet1

Then put a checkmark next to Enable Telnet service, and click the Apply button.

nagiosds415_enable_telnet2

Verify that the computer that you intend to use has a Telnet client.  For Windows 7, access the Programs link in the Control Panel, and then click the Turn Windows features on or off link.  Make certain that there is a checkmark next to Telnet Client, then click the OK button.

nagiosds415_enable telnet_computer

Open a command line (in Windows, Start – Run – type  cmd  and press the Enter key).  On the command line, type telnet followed by either the name of the DiskStation or the IP address of the DiskStation, then press the Enter key.  When prompted for a username, type root and press the Enter key.  Type the admin user’s password (that is used to access the DSM interface in a web browser) and press the Enter key  For example, if the DiskStation has an IP address of 192.168.1.55, the screen would appear similar to the following just before pressing the enter key:.

nagiosds415_telnet_to_diskstation

The command line on the DiskStation is very similar to the command line on a Unix or Linux computer, and is somewhat similar to a Windows command line or MS-DOS command line (use / rather than \, use ls rather than dir, use vi rather than edit):

nagiosds415_telnet_to_diskstation_command_test

We first need to add ipkg support to the DiskStation, detailed directions may be viewed at this link.  The exact directions may be different for other DiskStation models, but the following directions work for the DS415+, DS1813+ and DS412+ (note that all files downloaded from the Internet will be placed on volume1 in a new downloads directory – copy and paste the lines to the Telnet session, one line at a time – simply right-click inside the telnet window to paste, if you are using Windows 7/8’s telnet client):

cd /volume1
mkdir downloads
cd downloads
wget http://ipkg.nslu2-linux.org/feeds/optware/syno-i686/cross/unstable/syno-i686-bootstrap_1.2-7_i686.xsh
chmod +x syno-i686-bootstrap_1.2-7_i686.xsh
sh syno-i686-bootstrap_1.2-7_i686.xsh

The vi editor is used on the DiskStation to modify files; that vi editor is a bit challenging to use at first sight, so you may need help with a couple of basic commands (see this quick reference for other commands).  The commands in vi are case sensitive (i is not the same as I).  When a file is opened, press the i key on the keyboard to allow making changes to the file (such as typing commands, or deleting commands).  When finished making changes to the file press the Esc key.  Once the Esc key is pressed, type ZZ to save the changed file and quit, or :q! to quit without saving the changes.

Next, we must modify the file that establishes the environment for the root user, when that user connects to the DiskStation.  This change is needed as part of the ipkg installation.  Edit the .profile file used by the root user:

vi /root/.profile

Press the i key to switch to insert mode, place a # character in front of the two lines that contain the word PATH, and save the file (press the Esc key then type ZZ):

nagiosds415_comment_out_path

Reboot the DiskStation using the Synology interface by clicking the Options button (the head shaped icon) near the top-right, and then click Restart and confirm that the DiskStation should be restarted:

nagiosds415_restart diskstation

Once the Synology reboots, reconnect a telnet session to the Synology.  Update ipkg support and install some ipkg items that will be either directly used by Nagios, or during the installation of Nagios.  When installing the optware-devel package, an error may appear stating that there is an incompatibility between wget and wget-ssl – simply ignore that error.  An error will also appear when installing Apache – that problem will be fixed in the step that follows.

ipkg update
ipkg list
ipkg update wget-ssl
ipkg install optware-devel
ipkg install gcc
ipkg install libtool
ipkg install MySQL
ipkg install openssl
ipkg install openssl-dev
ipkg install sendmail
ipkg install inetutils
ipkg install apache

The ipkg install apache command will likely result in an error related to ext_filter_module.  Edit /opt/etc/apache2/httpd.conf using vi, and put a # character in front of the line LoadModule ext_filter_module libexec/mod_ext_filter.so :

nagiosds415_comment_out ext_filter_module

Save the file, and re-execute the ipkg install apache command.

ipkg install apache

Download the compilers for the Avoton CPU, which is installed in the DS415+ (the second compiler downloaded below will be used with the Nagios install – the first compiler typically resulted in errors (ex: ../lib/libnagios.a: could not read symbols: Archive has no index; run ranlib to add one) when compiling the Nagios program):

cd /volume1/downloads
wget http://sourceforge.net/projects/dsgpl/files/DSM%205.1%20Tool%20Chains/Intel%20x86%20Linux%203.2.40%20%28Avoton%29/gcc473_glibc217_x86_64_avoton-GPL.tgz
tar zxpf gcc473_glibc217_x86_64_avoton-GPL.tgz -C /usr/local/
wget http://sourceforge.net/projects/dsgpl/files/DSM%205.1%20Tool%20Chains/Intel%20x86%20Linux%203.2.40%20%28Avoton%29/gcc463_glibc213_i686_avoton-GPL.tgz 
tar zxpf gcc463_glibc213_i686_avoton-GPL.tgz -C /usr/local/

The above command will create two directory structures named x86_64-pc-linux-gnu and i686-pc-linux-gnu in the /usr/local directory.  Edit the .profile file to remove the # character that was earlier added in front of the lines containing the word PATH, save the file, then reboot the Synology (see the earlier provided screen captures for the lines that need to be modified, and the process for rebooting the Synology):

vi /root/.profile

Reconnect to the DiskStation using Telnet after the Synology reboots, and compile libltdl – note that the PATH modification below seems to be necessary to avoid an ldconfig: command not found error or a message about Perl not being found:

PATH=$PATH:/opt/bin/
cd /opt/share/libtool/libltdl/
env CC=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-gcc \
LD=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ld \
RANLIB=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ranlib \
CFLAGS="-I/usr/local/i686-pc-linux-gnu/include" \
LDFLAGS="-L/usr/local/i686-pc-linux-gnu/lib" \
./configure --host=i686-pc-linux-gnu --target=i686-pc-linux-gnu --build=i686-pc-linux-gnu -–prefix=/opt
 
make all
make install

Note that the following message may appear later when we attempt to verify that Nagios compiled correctly:

/opt/bin/nagios: error while loading shared libraries: libltdl.so.3: cannot open shared object file: No such file or directory

To avoid that error, execute the following commands to copy a couple of files to the correct locations (if the files do not already exist in those locations):

cp /opt/lib/libltdl.so.3 /opt/local/lib/libltdl.so.3
cp /opt/lib/libltdl.so.3 /usr/lib/libltdl.so.3
cp /opt/lib/libltdl.so /usr/lib/libltdl.so

Using the DiskStation’s Control Panel, click Group, and create a nagios group – the group does not require any special DiskStation permissions.

nagiosds415_create_nagios_group

Using the DiskStation’s Control Panel, click User and create a nagios user and add that user to the nagios group. The nagios user does not require any specific DiskStation permissions.

Next, switch back to the telnet session, download the Nagios source code, and compile the source code (note that if a mistake is made, and the nagios-4.0.8 directory must be removed, change to the /volume1/downloads directory and use this command to remove the directory, then re-execite the tar command: rm -rf nagios-4.0.8 ):

cd /volume1/downloads
wget http://sourceforge.net/projects/nagios/files/nagios-4.x/nagios-4.0.8/nagios-4.0.8.tar.gz
tar xzf nagios-4.0.8.tar.gz
cd nagios-4.0.8
 
env CC=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-gcc \
LD=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ld \
RANLIB=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ranlib \
CFLAGS="-I/usr/local/i686-pc-linux-gnu/include" \
LDFLAGS="-L/usr/local/i686-pc-linux-gnu/lib" \
./configure --host=i686-pc-linux-gnu --target=i686-pc-linux-gnu --build=i686-pc-linux-gnu -–prefix=/opt --with-command-group=nagios
 
make all
make install
make install-init
make install-config
make install-commandmode

Try running the Nagios verify command by executing the command on the following line (a Segmentation fault (core dumped) error message is bad news, which may indicate that the wrong compiler was used):

/opt/bin/nagios -v /opt/etc/nagios.cfg

If the compile process worked correctly, the telnet session should appear similar to below when the above verify command is executed:

nagiosds415_verify_ok

If the above command was successful, copy the Nagios startup script to the correct location so that Nagios will automatically start when the DiskStation is rebooted:

cp /usr/local/etc/rc.d/nagios /opt/etc/init.d/S81nagios

Verify that the ownership of the nagios directory is set correctly:

chown nagios:nagios /opt/bin/nagios -R

In addition to the main /opt/etc/nagios.cfg Nagios file, there are several other configuration files that are potentially used by Nagios (defined in the nagios.cfg file) – we will take a look at those files later:

/opt/etc/objects/commands.cfg
/opt/etc/objects/contacts.cfg
/opt/etc/objects/timeperiods.cfg
/opt/etc/objects/templates.cfg
/opt/etc/objects/localhost.cfg
/opt/etc/objects/windows.cfg
/opt/etc/objects/server.cfg
/opt/etc/objects/switch.cfg
/opt/etc/objects/printer.cfg

We need to make a couple of adjustments in the  /opt/etc/objects/commands.cfg file.

vi /opt/etc/objects/commands.cfg

Change the ‘notify-host-by-email’ command definition section as follows:

define command{
    command_name notify-host-by-email
    command_line /usr/bin/printf "%b" "Subject: $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /opt/sbin/sendmail -vt $CONTACTEMAIL$
    }

Change the ‘notify-service-by-email’ command definition section as follows:

define command{
    command_name notify-service-by-email
    command_line /usr/bin/printf "%b" "Subject: $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$\n" | /opt/sbin/sendmail -vt $CONTACTEMAIL$
    }

Change the ‘check_ping’ command definition section as follows (feel free to read the documentation for check_ping and specify different values):

define command{
        command_name    check_ping
        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000,25% -c 5000,90% -p 3 
        }

Save the file and exit vi.

(This part still needs some fine tuning to make the web interface work with Nagios.)  Edit the Nagios Makefile and change the line beginning with HTTPD_CONF to show HTTPD_CONF=/opt/etc/apache2/conf.d  Then save the file.

cd /volume1/downloads/nagios-4.0.8
vi Makefile

nagiosds415_change_httpd_conf

Execute the following command:

make install-webconf

Create a nagiosadmin user for the web administration, and specify a password when prompted (edit: January 11, 2015: the file location specified by the command below is incorrect if you plan to use the Nagios monitoring web pages – if you intend to use the Nagios monitoring web pages, execute the command as shown below, and we will later recreate the file in the correct location):

htpasswd -c /usr/local/etc/htpasswd.users nagiosadmin

Next, we need to start setting up the plugins for Nagios.  First the net-snmp source code is downloaded and extracted:

cd /volume1/downloads
wget http://sourceforge.net/projects/net-snmp/files/net-snmp/5.7.2/net-snmp-5.7.2.tar.gz
tar xzf net-snmp-5.7.2.tar.gz
cd net-snmp-5.7.2

Execute the following to compile the net-snmp source (note that this command uses the second compiler that was downloaded):

env CC=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-gcc \
LD=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ld \
RANLIB=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ranlib \
CFLAGS="-I/usr/local/i686-pc-linux-gnu/include" \
LDFLAGS="-L/usr/local/i686-pc-linux-gnu/lib" \
./configure --host=i686-pc-linux-gnu --target=i686-pc-linux-gnu --build=i686-pc-linux-gnu -–prefix=/opt

Several prompts will appear on the screen when the command is executed.  I entered the following for the prompts:

Default version of SNMP to use (3): 3
System Contact Information: (Enter)
System Location (Unknown): (Enter)
Location to write logfile (/var/log/snmpd.log): /opt/var/snmpd.log
Location to write persistent information (/var/net-snmp): (Enter)

Two additional commands to execute:

make -i
make install -i

Now we need to download the source code for the Nagios plugins (check_apt, check_breeze, check_by_ssh, check_clamd, check_cluster, check_dhcp, check_disk, check_disk_smb, check_dns, check_dummy, check_file_age, check_flexlm, check_ftp, check_http, check_icmp, check_ide_smart, check_ifoperstatup, check_ifstatus, check_imap, check_ircd, check_jabber, check_ldap, check_ldaps, check_load, check_log, check_mailq, check_mrtg, check_mrtgtraf, check_mysql, check_mysql_query, check_nagios, check_nntp, check_nntps, check_nt, check_ntp, check_ntp_peer, check_ntp_time, check_nwstat, check_oracle, check_overcr, check_ping, check_pop, check_procs, check_real, check_rpc, check_sensors, check_simap, check_smtp, check_snmp, check_spop, check_ssh, check_ssmtp, check_swap, check_tcp, check_time, check_udp, check_ups, check_users, check_wave) that allow Nagios to perform various monitoring tasks:

cd /volume1/downloads
wget https://www.nagios-plugins.org/download/nagios-plugins-2.0.3.tar.gz
tar xzf nagios-plugins-2.0.3.tar.gz
cd nagios-plugins-2.0.3
PATH=$PATH:/opt/bin/

Now compile the Nagios plugins:

env CC=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-gcc \
LD=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ld \
RANLIB=/usr/local/i686-pc-linux-gnu/bin/i686-pc-linux-gnu-ranlib \
CFLAGS="-I/usr/local/i686-pc-linux-gnu/include" \
LDFLAGS="-L/usr/local/i686-pc-linux-gnu/lib" \
./configure --with-openssl=/usr/syno/bin/openssl --with-nagios-user=nagios --with-nagios-group=nagios --with-ping-command="/opt/bin/ping -c %d %s" --psdir=/bin --with-ps-varlist="&procpid,&procppid,&procvsz,&procrss,procprog,&pos" --with-ps-cols=6 --with-ps-format="%d %d %d %d %s %n" --with-ps-command="/bin/ps -w"  --host=i686-pc-linux-gnu --target=i686-pc-linux-gnu --build=i686-pc-linux-gnu -–prefix=/opt
 
make -i
make install -i

At this point, the Nagios network monitoring utility will likely experience an error similar to the following when attempting to send an alert email:

output=collect: Cannot write ./dfr6BFFPC7027203 (bfcommit, uid=1026, gid=25): Permission denied

Execute the following commands, which should fix the above problem:

chmod g+w /opt/var/spool/clientmqueue
chmod 444 /opt/etc/mail/*.cf
chmod 7555 /opt/sbin/sendmail

We will need to use su to test the execution of various commands as the nagios user. Without the following fix (described here), you might see the following error message when attempting to execute a command as the nagios user:

su: warning: cannot change directory to /var/services/homes/nagios: No such file or directory su: /sbin/nologin: No such file or directory

Enter the following commands:

mkdir /var/services/homes
mkdir /var/services/homes/nagios
chown nagios:nagios /var/services/homes/nagios -R
vi /etc/passwd

Locate the line in the passwd file for the Nagios user.  Near the end of the line, /sbin/nologin should appear.  Replace that text with /bin/ash then save and exit vi.

Verify that the Nagios user is able to execute the check_ping plugin.  Replace MyDeviceHere with either an IP address or a network device name (such as localhost) that is on your network:

su - nagios -c "/opt/libexec/check_ping -H MyDeviceHere -w 5000,80% -c 5000,80% -p 5"

If there were no problems, then something similar to the following should appear (a Segmentation fault (core dumped) error message is bad news, which may indicate that the wrong compiler was used):

nagiosds415_check_ping_test

If the ping command (called by check_ping) is not able to resolve a network device name, and the fully qualified DNS name was not specified (MyDeviceHere.MyDomainHere.com), edit the /etc/resolv.conf file:

vi /etc/resolv.conf

On a new line in the file, add the following line (replacing MyDomainHere.com with your DNS domain name for the network):

search MyDomainHere.com

Verify that sendmail works for the Nagios user.  At the prompt that appears, type a short message, press the Enter key, type a period, then press the Enter key again – replace MyEmailAddressHere@MyDomainHere.com with your email address):

su - nagios -c "/opt/sbin/sendmail -vt MyEmailAddressHere@MyDomainHere.com"

If you see the following message, then there are two problems:

/opt/etc/mail/sendmail.cf: line 73: fileclass: cannot open '/opt/etc/mail/local-host-names': Group writable directory
WARNING: local host name (DS415) is not qualified; see cf/README: WHO AM I?

To fix the second problem (the warning), edit the /etc/hosts file using vi. Locate the line with the Synology’s IP address and the Synology’s name (for example DS415).  Between those two entries add the Synology’s name followed by a period and the domain name.  For example, the line may look like the following once edited.  Save the file and exit vi:

192.168.1.55   DS415.MyDomainHere.com DS415

To fix the “cannot open… Group writable directory” error, try the following (note that it is possible that only the first line is necessary, so try the sendmail test ago after executing just the first line below):

chmod 555 /opt/etc
chmod 755 /opt/etc/mail
chmod 444 /opt/etc/mail/local-host-names

Then repeat the email test by executing:

su - nagios -c "/opt/sbin/sendmail -vt MyEmailAddressHere@MyDomainHere.com"

—-

As a general tip, it is important to always verify the Nagios configuration before starting (or restarting after a configuration change) Nagios.  To verify the Nagios configuration type the following:

/opt/bin/nagios -v /opt/etc/nagios.cfg

Once the monitoring target devices are defined, it is possible to start Nagios as a background task (daemon) by executing the following:

/opt/bin/nagios -d /opt/etc/nagios.cfg

To stop Nagios that is executing as a background task, use the ps command to locate the Nagios process, find the lowest process ID (PID) for the line that includes nagios.cfg (17346 in this case), then kill the process that is associated with that PID:

ps | grep 'nagios'

nagiosds415_stop_nagios_daemon

At this point, Nagios will hopefully run as a background task, and it should be able to ping and send email alerts.  However, if you were following the above directions, we have not yet instructed Nagios which devices to monitor, and to whom the alert emails should be sent.  The next step is to define the email contacts by modifying the /opt/etc/objects/contacts.cfg file (see the documentation for assistance):

vi /opt/etc/objects/contacts.cfg

After setting up the contacts, we should probably tell Nagios which devices to monitor.  If there are a lot of devices on your network to be monitored, you might find that using Microsoft Excel rather than vi to create the object definitions makes the task more manageable.  See the previous article for the steps to use Microsoft Excel.

If you decided to use some of the non-standard Nagios group names (as I did), those non-standard group names must be defined in the /opt/etc/objects/templates.cfg file:

vi /opt/etc/objects/templates.cfg

A portion of the additional entries that I made in this file include the following:

define host{
       name                    ap      ; The name of this host template
       use                     generic-host    ; Inherit default values from the generic-host temp
       check_period            24x7            ; By default, access points are monitored round t
       check_interval          5               ; Actively check the access point every 5 minutes
       retry_interval          1               ; Schedule host check retries at 1 minute intervals
       max_check_attempts      10              ; Check each access point 10 times (max)
       check_command           check_ping      ; Default command to check if access points are "alive"
       notification_period     24x7            ; Send notification out at any time - day or night
       notification_interval   30              ; Resend notifications every 30 minutes
       notification_options    d,r             ; Only send notifications for specific host states
       contact_groups          admins          ; Notifications get sent to the admins by default
       hostgroups              ap ; Host groups that access points should be a member of
       register                0               ; DONT REGISTER THIS - ITS JUST A TEMPLATE
       }

define host{
       name                    camera  ; The name of this host template
       use                     generic-host    ; Inherit default values from the generic-host temp
       check_period            24x7            ; By default, cameras are monitored round t
       check_interval          60              ; Actively check the device every 60 minutes
       retry_interval          1               ; Schedule host check retries at 1 minute intervals
       max_check_attempts      10              ; Check each device 10 times (max)
       check_command           check_ping      ; Default command to check if device are "alive"
       notification_period     24x7            ; Send notification out at any time - day or night
       notification_interval   240             ; Resend notifications every 240 minutes
       notification_options    d,r             ; Only send notifications for specific host states
       contact_groups          admins          ; Notifications get sent to the admins by default
       hostgroups              camera ; Host groups that cameras should be a member of
       register                0               ; DONT REGISTER THIS - ITS JUST A TEMPLATE
       }

Nagios will not know that it should read the additional configuration files until it is told to do so by modifying the /opt/etc/nagios.cfg file.

vi /opt/etc/nagios.cfg

If you have selected to use any of the custom files that were created based on my previous article, instruct Nagios to read the associated file by adding entries to the nagios.cfg file:

# Charles Hooper's object types
cfg_file=/opt/etc/objects/ap.cfg
cfg_file=/opt/etc/objects/camera.cfg
cfg_file=/opt/etc/objects/computer.cfg
cfg_file=/opt/etc/objects/external.cfg
cfg_file=/opt/etc/objects/other.cfg
cfg_file=/opt/etc/objects/printer.cfg
cfg_file=/opt/etc/objects/server.cfg
cfg_file=/opt/etc/objects/switch.cfg

A large number of changes were likely made to the Nagios configuration files, so it is important to verify that there are no errors in the configuration:

/opt/bin/nagios -v /opt/etc/nagios.cfg

If no errors are found in the configuration, terminate (kill) nagios and then restart Nagios as described above.

—-

(Added January 11, 2015)

While trying to find a way to allow the Nagios status web pages to work on the DS415+, I discovered that Nagios was not running in daemon mode once I had made entries into the various configuration files to specify the devices to be monitored.  Taking a look at my previous article, I quickly found the solution for the daemon mode problem.  In a telnet session enter the following:

mkdir /opt/var/nagios
mkdir /opt/var/nagios/archives
mkdir /opt/var/nagios/spool
mkdir /opt/var/nagios/spool/checkresults
mkdir /opt/var/nagios/rw/
chown nagios:nagios /opt/var/nagios -R
chmod g+rwx /opt/var/nagios/rw
chmod g+s /opt/var/nagios/rw
vi /opt/etc/nagios.cfg

In the nagios.cfg file, I made the following changes:

log_file=/opt/var/nagios/nagios.log
object_cache_file=/opt/var/nagios/objects.cache
precached_object_file=/opt/var/nagios/objects.precache
status_file=/opt/var/nagios/status.dat
command_file=/opt/var/nagios/rw/nagios.cmd
lock_file=/opt/var/nagios/nagios.lock
temp_file=/opt/var/nagios/nagios.tmp
log_archive_path=/opt/var/nagios/archives
check_result_path=/opt/var/nagios/spool/checkresults
state_retention_file=/opt/var/nagios/retention.dat
debug_file=/opt/var/nagios/nagios.debug

Save and exit vi.  If the Nagios daemon is running in the background, find the process and kill it (replace 24532 with the process ID of the first line that contains /opt/bin/nagios -d /opt/etc/nagios.cfg):

ps | grep 'nagios'
kill 24532

Verify the Nagios configuration, and if there were no problems, then start Nagios in daemon mode:

/opt/bin/nagios -v /opt/etc/nagios.cfg
/opt/bin/nagios -d /opt/etc/nagios.cfg

Check the Nagios log file for problems, displaying just the last 100 lines:

tail -n 100 /opt/var/nagios/nagios.log

I never bothered to determine how to make the Nagios monitoring status web pages work with the Synology DS412+, DS1813+, and DS212+.  I thought that I would see what steps would be required to make that functionality work on a Synology DS415+ (note that the following steps may work exactly the same on the Synology DS412+, DS1813+, and DS212+). The Synology DSM operating system utilizes port 5000 for web traffic.  If you add the WordPress package to the Synology, that package uses the standard port 80 for web traffic.  If you followed the above steps for installing Nagios, you installed a second copy of the Apache web server on the Synology that uses port 8000 for web traffic.  If your Synology has an IP address of 192.168.1.60, then you would use the following website address to access the second web server on the Synology: http://192.168.1.60:8000/nagios/

The Nagios monitoring status web pages, once set up, will appear as a directory of that second web server, as shown below:

nagiosds415_web_status

The value of the Nagios monitoring status web pages becomes apparent fairly quickly by reviewing the above screen capture.  The above Tactical Overview shows that there are 18 monitored devices that are up, one monitored device that is down, and another device that is in the process of being checked.  The links at the left provide additional information about the monitored devices.  Let’s configure the second copy of Apache on the Synology to support the Nagios monitoring status web pages.

The second copy of Apache uses the configuration file /opt/etc/apache2/httpd.conf.  Edit that file:

vi /opt/etc/apache2/httpd.conf

Add the following directly below the # Supplemental configuration heading, which is near the end of the file:

# Added by per Charles Hooper's Nagios installation guide for Synology DS415+
Include etc/apache2/conf.d/nagios.conf
Include etc/apache2/conf.d/php.conf

Save the file and exit vi.  Next, we need to create the htpasswd.users file in the correct location.  In the above steps, we executed the following command:

/opt/sbin/htpasswd -c /usr/local/etc/htpasswd.users nagiosadmin

That htpasswd.users file is expected to be in the /opt/etc directory.  Execute the following command to create the file in the correct directory (you will be prompted for a password for the nagiosadmin user):

/opt/sbin/htpasswd -c /opt/etc/htpasswd.users nagiosadmin

The Nagios monitoring status web pages require PHP support to be added to the second Apache installation.  Execute the following commands to install PHP:

/opt/bin/ipkg update
/opt/bin/ipkg list
/opt/bin/ipkg install php
/opt/bin/ipkg install php-apache

Next, we need to modify the /opt/etc/apache2/conf.d/nagios.conf file that was created during the Nagios installation.

vi /opt/etc/apache2/conf.d/nagios.conf

Below the heading <Directory “/opt/share”> add the following line:

   DirectoryIndex index.php

Save the file and exit vi.  Next, we need to adjust the php.ini file on the Synology:

vi /opt/etc/php.ini

In the file (near line 108), locate the following line:

output_buffering = Off

Change that line to show:

output_buffering = 8192

Locate the following line (near line 248) in the file:

memory_limit = 8M

Change that line to show:

memory_limit = 128M

There may be a second php.ini file located in the /etc/php directory, make the same change to that file, then save the file and exit vi:

vi /etc/php/php.ini

Perform a graceful restart of the second copy of Apache:

/opt/sbin/apachectl -k graceful

Try to access the Nagios status monitoring pages in a web browser (replace 192.168.1.60 with the IP address of your Synology)  http://192.168.1.60:8000/nagios/

You should be prompted to enter a username and password.  Enter nagiosadmin for the username, and enter the password for that user that you created when executing the /opt/sbin/htpasswd command earlier.  If the web page does not display, take a look at the last 100 lines of the Apache error log to see if any clues are provided:

tail -n 100 /opt/var/apache2/log/error_log

Some of the interesting error messages that I experienced including the following:

[Sun Jan 04 14:07:55 2015] [error] [client 192.168.1.218] (2)No such file or directory: Could not open password file: /opt/etc/htpasswd.users
 
[Sun Jan 04 14:23:04 2015] [error] [client 192.168.1.218] Directory index forbidden by Options directive: /opt/share/
 
[Sun Jan 04 15:07:09 2015] [error] [client 192.168.1.218] File does not exist: /opt/share/<, referer: http://192.168.1.60:8000/nagios/
 
[Sun Jan 04 17:53:06 2015] [notice] child pid 15126 exit signal Segmentation fault (11)
 
[Sun Jan 11 09:41:57 2015] [error] [client 192.168.1.213] script not found or unable to stat: /opt/sbin/statusmap.cgi, referer: http://192.168.1.60:8000/nagios/side.php

The last of the above errors still remains, the file /opt/sbin/statusmap.cgi does not exist.  For now, I will let someone else find a solution for that problem.

If you find that a monitored device is down, using the web interface it is possible to disable future checking of the device, as well as alter a number of other notification options.  Unfortunately, clicking any of the options under the Host Commands heading will result in a permission error.

nagiosds415_change_notifications

To fix the permission error:

Using the Synology Control Panel interface, create a new group named www – this group requires no specific Synology permissions.  Next, using the Synology Control Panel interface, create a user named apache – make this user a member of the nagios, users, and www groups.  This user requires no specific Synology permissions.  Then change the username and group under which Apache executes:

vi /opt/etc/apache2/httpd.conf

In the httpd.conf file, locate the following two lines:

User nobody
Group #-1

Change those lines to appears as follows:

User apache
Group www

Save the file and exit vi.  Gracefully restart the second copy of Apache:

/opt/sbin/apachectl -k graceful

Refresh the Nagios monitoring status web page – the options under the Host Commands heading should no longer return an error.

—-


Adding Plex Support (Added December 29, 2014)

Note: Some of these steps may use utilities that were installed during the Nagios install.  Plex does not yet support the CPU type in the DS415+, so it is necessary to modify a configuration file during the install.  (Note May 25, 2015: The Plex download version 0.9.12.1.1079 natively supports the DS415+’s CPU, so after downloading the file it is possible to skip to adjusting the Trust Level setting step in the Synology Package Center.)

Connect to the DS415+ using a telnet session and the root username and password (see the directions above, if the steps are not clear).  Change to the downloads directory that was created above, and use the wget command to download what is as of today the current version of Plex:

cd /volume1/downloads/
wget https://downloads.plex.tv/plex-media-server/0.9.11.7.803-87d0708/PlexMediaServer-0.9.11.7.803-87d0708-x86.spk

Create a directory, extract the downloaded PlexMediaServer-0.9.11.7.803-87d0708-x86.spk file to the just created directory, then switch to that directory:

mkdir PlexMediaServer-0.9.11.7.803-87d0708-x86
tar -xvf PlexMediaServer-0.9.11.7.803-87d0708-x86.spk -C /volume1/downloads/PlexMediaServer-0.9.11.7.803-87d0708-x86
cd PlexMediaServer-0.9.11.7.803-87d0708-x86

Next, we need to edit the INFO file to add support for the CPU that is installed in the DS415+:

vi INFO

If you are not familiar with using vi to edit files, you will need to switch vi to edit mode by typing i on the keyboard (note that is a lowercase i) before attempting to make any changes to the file.  When you are finished making changes, press the Esc key on the keyboard, then type ZZ to save and exit (note that is uppercase ZZ).

In the file, change the line:

arch="x86 cedarview bromolow evansport"

to:

arch="x86 cedarview bromolow evansport avoton"

When finished with the edit, the file should appear as below.  Save the file and exit vi.

plexds415_modify_arch

Next, we need to create a new spk file that includes the modified INFO file, and copy that file to a shared folder (mysharedfolder in this example) that was previously created on the DS415+ that is accessible from a computer that has access to the DS415+’s DSM interface.

tar -cvf /volume1/downloads/PlexMediaServerMod-0.9.11.7.803-87d0708-x86.spk *
cd ..
cp PlexMediaServerMod-0.9.11.7.803-87d0708-x86.spk /volume1/mysharedfolder/PlexMediaServerMod-0.9.11.7.803-87d0708-x86.spk

Now on the computer that has access to the DS415+’s DSM interface, click Package Center, then click Settings.  Under the Trust Level heading, change the setting from Synology Inc. to “Any publisher“, then click OK.

plexds415_modify_package_settings

Next, install Plex using the Manual Install feature.  Click the Manual Install button, then click the Browse button.  Switch to the shared folder where the spk file was copied (mysharedfolder in the above example), and select to open the PlexMediaServerMod-0.9.11.7.803-87d0708-x86.spk file.  Click the Next button to complete the install of Plex.

plexds415_manual_install

I have only just started experimenting with Plex, so I do not have a full grasp of its capabilities yet.  There are several “channels” that can be added to watch certain types of recorded video.  After experimenting with a couple of the easily accessible channels, I stumbled across this page, which described how to add several additional “unsupported” channels.  The following steps seem to work to install the additional channels from a telnet session connected to the DS415+.  Download and extract the unsupported channels:

cd /volume1/downloads/
wget https://dl.dropboxusercontent.com/u/15227710/UnSupportedAppstore.bundle.zip
mkdir plexunsupported
unzip UnSupportedAppstore.bundle.zip -d ./plexunsupported/

Change to the directory where the Plex plugins are stored, create a directory for the new plugin that was just downloaded and extracted, copy the downloaded plugin files to the directory that was just created, then change the ownership of the downloaded and extracted files to the plex user:

cd /volume1/Plex/Library/Application\ Support/Plex\ Media\ Server/Plug-ins/
mkdir UnSupportedAppstore.bundle
cp -r /volume1/downloads/plexunsupported/UnSupportedAppstore.bundle/* ./UnSupportedAppstore.bundle
chown -R plex:users ./UnSupportedAppstore.bundle

As far as I could determine, the unsupported channels will only appear when the Plex package is stopped and then started again.  In the Package Center’s Installed list click Plex Media Server.  Click the down arrow to the right of Action, then click Stop.  Wait 30 seconds, then click the down arrow again and select Run.

plexds415_restart_plex

If the DS415+ is named ds415, the it should be possible to access Plex on the Synology using this link http://ds415:32400/web/index.html (replace ds415 with the unit’s IP address if the unit’s network name is not known).  Clicking the Channels link at the left should show the Unsupported AppStore (the black icon below with the white border)

plexds415_after_restart_unsupported1

Clicking the Unsupported AppStore icon should then display a list of categories at the left.  The New category contains quite a few channels, as shown below.  To add the channel, click the channel, then click the Install link.

plexds415_after_restart_unsupported2

I am sure that there is much more to Plex than just offering an alternative to a Roku player or the streaming video options of some Blu-Ray players and network capable TVs (which are also able to retrieve the channels from Plex), but I have not had much time to experiment yet.  I understand that Plex is very good at transcoding video streams for different media consumption devices, such as tablets, but I am not sure that I have a use for that functionality yet.

—-


Plex/Nagios/Wordpress Stress Test (Added April 5, 2014)

A question was asked on the Synology forums whether or not I had any evidence that replacing the pre-installed 2GB of memory with an 8GB Crucial memory card made a measurable difference in the Synology DS415+’s performance.  That is a very difficult question to answer – the answer will depend largely on how the DS415+ is used.  If the DiskStation is only used to receive 10+ GB files from a single client, the additional memory probably will make no difference in the performance of the DS415+.

When the DS415+ was first released there were several discussion threads on the Synology forums indicating that the Synology DS415+ was not powerful enough to handle 1080p video for clients wishing to play back that video on various streaming devices, while the less powerful DS415Play could support 1080P playback for clients because it had additional coprocessors to support video transcoding.  So, I thought that I would try a quick test with the DS415+.  The DS415+ is running Nagios to monitor several devices, including 15+ high definition security cameras, as well as acting as the FTP target for those security cameras.  The DS415+ is also running WordPress, although WordPress has not seen much action lately on the unit.  Plex is occasionally used on the DS415+, but had not been used in the last 10 to 12 hours.  The DS415+ with 8 GB of memory installed showed the following memory statistics in its Resource Monitor: Reserved: 217.6 MB, Used: 741.9 MB,  Buffer: 151.7 MB, Cached: 6.8 GB, Free: 166.5 MB.  So, most of the additional 6 GB of memory was used for file caching, which helps speed up the apparent write speed of files, and the read speed of frequently accessed files.

So, I opened the WordPress website on the DS415+ and viewed a couple of pages.  I then went to the Plex website hosted on the DS415+ using a Window 8.1 tablet and requested the playback of a 1080P movie – the tablet supports 1080P video.  The DS415+ still had a bit of CPU capacity left, and the video was playing back smoothly on the tablet.  Just for fun I then used a Sony Blu-Ray player to request a different 1080P movie from the DS415+ while the first movie was still streaming to the tablet.  Below is a screen capture of a portion of the DSM interface on the DS415+ while both video streams were playing back (with occasional buffering):

plexds415_plex_playback_2_clients

I still cannot say for certain if the additional memory helped in this particular stress test.  However, for the current cost of the 8GB memory card ($55 USD), the additional memory could prove to be helpful depending on how the DS415+ is used – all that it would take in this case is for a WordPress cache optimizer to consume 400 MB of additional memory to push the DS415+ over 2 GB of total memory consumption.

July 19, 2013 (Modified July 27, 2013, July 28, 2013, November 19, 2013)

(Forward to the Next Post in the Series)

—-

Update July 27, 2013:

  • I now have Nagios running on an ARM based Synology DiskStation DS212+.  Most of the steps are the same as outlined below, however there are a few additional errors that must be addressed (see below additional steps).
  • All of the ./configure commands should have included –prefix=/opt (rather than –prefix=/usr/local or completely omitting that parameter).  That change eliminates the need to copy the Nagios plugins to the correct location.  Possibly related, the -i parameter was unnecessary for the snmp and Nagios plugins make and make install commands when the ./configure command included the –prefix=/opt prefix.
  • The wget http://sourceforge.net/projects/dsgpl/files/DSM%204.1%20Tool%20Chains/Intel%20×86%20Linux%203.2.11&#8230; download step for the gcc compiler is apparently unnecessary, at least on the Synology DiskStation DS212+ (see below).

—-

This article describes how to compile and run Nagios on a Synology DiskStation DS1813+ (64 bit) or Synology DiskStation DS412+ (32 bit, the 32 bit steps should also apply to the DS1812+) NAS, both of which utilize Intel Atom processors (cat /proc/cpuinfo indicates that the DS412+ is using a 2.13GHz Atom D2700, while the DS1813+ is using a 2.13GHz Atom D2701), and utilize the DSM 4.2 operating system.  Not all Synology DiskStation NAS devices use Intel based CPUs – some of the less expensive DiskStations use ARM type processors (see this link to determine the type of CPU installed in a specific DiskStation).  It may be possible to produce a working version of Nagios on NAS devices that do not have Intel 32 bit or 64 bit processors, but I have not yet fully tested the procedure.

Warning: A lot of what follows is based on experimentation, with the end goal of having Nagios running on a Synology DiskStation having the ability to ping devices on the network or the Internet, with an email sent to an administrator when a device stops responding to ping requests, and to send a second email when the device resumes responding to ping requests.  This functionality represents a small fraction of Nagios’ capabilities through the use of plugins.  File paths vary from one Linux distribution to the next, so that adds a bit of challenge to make certain that the files are placed in the required directory.  Copying a file to the wrong directory may temporarily disable the DiskStation and require the reinstallation of the Synology DSM operating system.  The directions below are not final, and quite likely do not represent the most efficient approaches to accomplish the end goal – but the directions will hopefully be “close enough to correct” to allow the average reader of this blog to ping and send email alerts from a DiskStation.

I have relied on the free Nagios network monitoring solution since 2002 to provide an early warning of problems associated with network attached equipment including servers, production floor computers, switches, printers, wireless access points, IP cameras, Internet connection stability, etc.  While I rely on Nagios’ alerting system, I am not an expert at configuring the Nagios network monitoring system; the Nagios configuration documentation may be downloaded here.

First, make certain that the Telnet Service (or SSH Service if that is preferred) is enabled on the DiskStation.  In the DiskStation’s Control Panel, click Terminal.

InstallNagiosDiskStation1

Place a checkmark next to Enable Telnet service (if the item is not already checked), and then click the Apply button.

InstallNagiosDiskStation2

Verify that the computer that you intend to use has a Telnet client.  For Windows 7, access the Programs link in the Control Panel, and then click the Turn Windows features on or off link.  Make certain that there is a checkmark next to Telnet Client, then click the OK button.

InstallNagiosDiskStation3

Open a command line (in Windows, Start – Run – type  cmd  and press the Enter key).  On the command line, type telnet followed by either the name of the DiskStation or the IP address of the DiskStation, then press the Enter key.  When prompted for a username, type root and press the Enter key.  Type the admin user’s password (that is used to access the DSM interface in a web browser) and press the Enter key.

InstallNagiosDiskStation4

The command line on the DiskStation is very similar to the command line on a Unix or Linux computer, and is somewhat similar to a Windows command line or MS-DOS command line (use / rather than \, use ls rather than dir, use vi rather than edit):

InstallNagiosDiskStation5

We first need to add ipkg support to the DiskStation, detailed directions may be viewed at this link.  The exact directions may be different for other DiskStation models, but the following directions work for both the DS1813+ and DS412+ (note that all files downloaded from the Internet will be placed on volume1 in the downloads directory – copy and paste the lines to the Telnet session, one line at a time):

cd /volume1
mkdir downloads
cd downloads
wget http://ipkg.nslu2-linux.org/feeds/optware/syno-i686/cross/unstable/syno-i686-bootstrap_1.2-7_i686.xsh
chmod +x syno-i686-bootstrap_1.2-7_i686.xsh
sh syno-i686-bootstrap_1.2-7_i686.xsh

The vi editor is used on the DiskStation to modify files; that vi editor is a bit challenging to use at first sight, so you may need help with a couple of basic commands (see this quick reference for other commands).  The commands in vi are case sensitive (i is not the same as I).  When a file is opened, press the i key on the keyboard to allow making changes to the file (such as typing commands, or deleting commands).  When finished making changes to the file press the Esc key.  Once the Esc key is pressed, type ZZ to save the changed file and quit, or :q! to quit without saving the changes.

Next, we must modify the file that establishes the environment for the root user, when that user connects to the DiskStation.  This change is needed as part of the ipkg installation.  Edit the .profile file used by the root user:

vi /root/.profile

Add a # character in front of the two lines that contain the word PATH, then save the file (see the brief directions above to switch between command and insert mode in vi):

InstallNagiosDiskStation6

Next, reboot the DiskStation by clicking the Restart button in the Synology DSM interface (note: it should be possible to type reboot in the Telnet interface, however the DiskStation locked up the one time I attempted to execute that command).

InstallNagiosDiskStation7

Once the DiskStation reboots, reconnect to the DiskStation using Telnet, connecting as the root user, just as was done earlier.

The ipkg command should now work on the command line.  First, request that an updated list of available packages is downloaded, then display that list of packages:

ipkg update
ipkg list

Next, download a couple of packages that will be used by the Nagios network monitoring tool.  Note that using ipkg to install packages is a lot easier than compiling source code, so have fun with the ipkg utility.  When installing the optware-devel package, an error may appear stating that there is an incompatibility between wget and wget-ssl – just ignore that error for now.

ipkg update wget-ssl
ipkg install optware-devel
ipkg install gcc
ipkg install libtool
ipkg install mysql

Next, we need to compile a file and copy a couple of files:

cd /opt/share/libtool/libltdl/
./configure --prefix=/opt
make all
make install

cp /usr/syno/apache/modules/mod_ext_filter.so /opt/libexec/mod_ext_filter.so
cp /usr/syno/apache/modules/*.* /opt/libexec/

Now, install the Apache package:

ipkg install apache

If an error message is displayed on screen about mod_ext_filter.so, then modify the /opt/etc/apache2/httpd.conf file and add a # in front of the line LoadModule ext_filter_module libexec/mod_ext_filter.so and save the file.  Re-execute the ipkg install apache command (note that the up arrow on the keyboard may be pressed to quickly retype one of the previously executed commands).

InstallNagiosDiskStation8

Using the DiskStation’s Control Panel, create a nagios group and a nagcmd group (the nagcmd group probably will not be used for anything specific).  These groups do not require any special DiskStation permissions.

InstallNagiosDiskStation9

Using the DiskStation’s Control Panel, create a nagios user and add that user to the nagios and nagcmd groups.  The nagios user does not require any specific DiskStation permissions.

Next, switch back to the Telnet session, download the Nagios source code, and compile the source code:

DiskStation DS212+ Notes:

The following ./configure call was used on the DS212+:

./configure --prefix=/opt --with-command-group=nagios --disable-nanosleep --enable-nanosleep=no

The ./configure aborted with the following error message:

checking for pthread_create in -lpthread... no
checking for pthread_mutex_init in -lpthread... no
checking for pthread_create in -lpthreads... no
checking for pthread_create in -llthread... no
checking if we need -pthread for threads... no
checking for library containing nanosleep... no
Error: nanosleep() needed for timing operations.

The test that threw the error is located roughly 63% of the way through the configure file (on roughly line 5635).  If the exit 1 line in the configure file is commented out, then the configure step will complete.  However, the make all command will then fail with the following error messages:

/volume1/downloads/nagios/base/nebmods.c:363: undefined reference to `dlclose'
nebmods.o: In function `neb_load_module':
/volume1/downloads/nagios/base/nebmods.c:218: undefined reference to `dlopen'
/volume1/downloads/nagios/base/nebmods.c:249: undefined reference to `dlsym'
/volume1/downloads/nagios/base/nebmods.c:266: undefined reference to `dlsym'
/volume1/downloads/nagios/base/nebmods.c:299: undefined reference to `dlsym'
/volume1/downloads/nagios/base/nebmods.c:225: undefined reference to `dlerror'
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/libpthread.so: undefined reference to `__default_sa_restorer_v2@GLIBC_PRIVATE'
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/libpthread.so: undefined reference to `__default_rt_sa_restorer_v2@GLIBC_PRIVAT
E'
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/libpthread.so: undefined reference to `__default_rt_sa_restorer_v1@GLIBC_PRIVAT
E'
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/lib/libpthread.so: undefined reference to `__default_sa_restorer_v1@GLIBC_PRIVATE'
collect2: ld returned 1 exit status
make[1]: *** [nagios] Error 1
make[1]: Leaving directory `/volume1/downloads/nagios/base'
make: *** [all] Error 2

After a bit of searching on the Internet, I found a page that suggested making the following changes (note that I unsuccessfully tried a couple of other steps that may have also partially corrected the issue):

mkdir /opt/arm-none-linux-gnueabi/lib_disabled
mv /opt/arm-none-linux-gnueabi/lib/libpthread* /opt/arm-none-linux-gnueabi/lib_disabled

cp /lib/libpthread.so.0 /opt/arm-none-linux-gnueabi/lib/
cd /opt/arm-none-linux-gnueabi/lib/
ln -s libpthread.so.0 libpthread.so
ln -s libpthread.so.0 libpthread-2.5.so

After making the above changes, I was able to run the configure and make all commands without receiving an error.

cd /volume1/downloads
wget http://prdownloads.sourceforge.net/sourceforge/nagios/nagios-3.5.0.tar.gz
tar xzf nagios-3.5.0.tar.gz
cd nagios
./configure --prefix=/opt --with-command-group=nagios
make all
make install
make install-init
make install-config
make install-commandmode

We apparently need to copy a couple of files to different locations at this point:

cp /opt/lib/libltdl.so.3 /opt/local/lib/libltdl.so.3
cp /opt/lib/libltdl.so.3 /usr/lib/libltdl.so.3
cp /opt/lib/libltdl.so /usr/lib/

Undo the changes that were earlier made to the /root/.profile file, where # characters were added in front of any line that contained the word PATH.  Remove those # characters and save the file:

vi /root/.profile

(This part still needs some fine tuning to make the web interface work with Nagios.)  Edit the Nagios Makefile and change the line beginning with HTTPD_CONF to show HTTPD_CONF=/opt/etc/apache2/conf.d  Then save the file.

cd /volume1/downloads/nagios
vi Makefile

InstallNagiosDiskStation10

Execute the following command:

make install-webconf

Create a nagiosadmin user for the web administration, specify a password when prompted:

htpasswd -c /usr/local/etc/htpasswd.users nagiosadmin

Update November 19, 2013:

GabrielM reported in a comment below that it may be necessary to specify the full path to the htpasswd program:

/usr/syno/apache/bin/htpasswd -c /usr/local/etc/htpasswd.users nagiosadmin

Install a couple of additional ipkg packages that will be used by Nagios (the last package adds a ping utility that may be used by Nagios – the security permissions on the DiskStation prevent non-root users from using the built-in ping utility):

ipkg install openssl
ipkg install openssl-dev
ipkg install sendmail
ipkg install inetutils

A step that may or may not be required is to download a functioning C++ compiler (some of the commands below point to files provided with the C++ compiler) – it appears that there should already be a compiler on the DiskStation at this point (in /opt/bin), so the successful completion of this task of downloading a usable C++ compiler might not be required.

DiskStation DS212+ Notes:

These wget and tar steps were completely skipped on the DS212+

For the DiskStation DS1813+ 64 bit:

cd /volume1/downloads
wget http://sourceforge.net/projects/dsgpl/files/DSM%204.1%20Tool%20Chains/Intel%20x86%20Linux%203.2.11%20%28Cedarview%29/gcc420_glibc236_x64_cedarview-GPL.tgz
tar zxpf gcc420_glibc236_x64_cedarview-GPL.tgz -C /usr/local/

For the DiskStation DS412+ 32 bit:

cd /volume1/downloads
wget http://sourceforge.net/projects/dsgpl/files/DSM%204.2%20Tool%20Chains/Intel%20x86%20Linux%203.2.11%20%28Bromolow%29/gcc421_glibc236_x86_bromolow-GPL.tgz
tar zxpf gcc421_glibc236_x86_bromolow-GPL.tgz -C /usr/local/

Now the net-snmp source code is downloaded and extracted:

DiskStation DS212+ Notes:

The ./configure call on the DS212 (might also work on the other DiskStation models):

./configure –prefix=/opt

The make call threw several errors, including:

/bin/sh: arm-none-linux-gnueabi-ld: not found
make[2]: *** [../blib/arch/auto/NetSNMP/default_store/default_store.so] Error 127

Before running the make command on the DS212+, execute the following command:

ln -s /opt/bin/ld /opt/bin/arm-none-linux-gnueabi-ld

The -i parameter may be omitted when running the make and make install commands.

cd /volume1/downloads
wget http://sourceforge.net/projects/net-snmp/files/net-snmp/5.7.2/net-snmp-5.7.2.tar.gz
tar xzf net-snmp-5.7.2.tar.gz
cd net-snmp-5.7.2

For the DiskStation DS1813+ 64 bit, execute the following to compile the net-snmp source (note that this command uses the compiler that was downloaded):

env CC=/usr/local/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc \
LD=/usr/local/x86_64-linux-gnu/bin/x86_64-linux-gnu-ld \
RANLIB=/usr/local/x86_64-linux-gnu/bin/x86_64-linux-gnu-ranlib \
CFLAGS="-I/usr/local/x86_64-linux-gnu/include" \
LDFLAGS="-L/usr/local/x86_64-linux-gnu/lib" \
./configure --host=x86_64-linux-gnu --target=x86_64-linux-gnu --build=x86_64-pc-linux --prefix=/usr/local

For the DiskStation DS412+ 32 bit, execute the following to compile the net-snmp source (note: I could not use any of the different compilers that I tried downloading due to the compilers crashing with one of two error messages, so this command uses the compiler in /opt/bin):

env CC=/opt/bin/i686-linux-gnu-gcc \
LD=/usr/local/i686-linux-gnu/bin/i686-linux-gnu-ld \
RANLIB=/usr/local/i686-linux-gnu/bin/i686-linux-gnu-ranlib \
CFLAGS="-I/usr/local/i686-linux-gnu/include" \
LDFLAGS="-L/usr/local/i686-linux-gnu/lib" \
./configure --host=i686-linux-gnu --target=i686-linux-gnu --build=i686-linux-gnu --prefix=/usr/local

Several prompts will appear on the screen when either of the two commands is executed.  I entered the following for the prompts:

Default version of SNMP to use (3): 3
System Contact Information: (Enter)
System Location (Unknown): (Enter)
Location to write logfile (/var/log/snmpd.log): /opt/var/snmpd.log
Location to write persistent information (/var/net-snmp): (Enter)

Two additional commands to execute:

make -i
make install -i

Now we need to download the source code for the Nagios plugins (check_apt, check_breeze, check_by_ssh, check_clamd, check_cluster, check_dhcp, check_disk, check_disk_smb, check_dns, check_dummy, check_file_age, check_flexlm, check_ftp, check_http, check_icmp, check_ide_smart, check_ifoperstatup, check_ifstatus, check_imap, check_ircd, check_jabber, check_ldap, check_ldaps, check_load, check_log, check_mailq, check_mrtg, check_mrtgtraf, check_mysql, check_mysql_query, check_nagios, check_nntp, check_nntps, check_nt, check_ntp, check_ntp_peer, check_ntp_time, check_nwstat, check_oracle, check_overcr, check_ping, check_pop, check_procs, check_real, check_rpc, check_sensors, check_simap, check_smtp, check_snmp, check_spop, check_ssh, check_ssmtp, check_swap, check_tcp, check_time, check_udp, check_ups, check_users, check_wave) that allow Nagios to perform various monitoring tasks:

cd /volume1/downloads
wget http://prdownloads.sourceforge.net/sourceforge/nagiosplug/nagios-plugins-1.4.16.tar.gz
tar xzf nagios-plugins-1.4.16.tar.gz
cd nagios-plugins-1.4.16/

Update November 19, 2013:

GabrielM reported in a comment below that the occasionally changing “current version” of the Nagios plugins makes it difficult to download the plugins from the source shown above.  If you open the http://prdownloads.sourceforge.net/sourceforge/nagiosplug/ web page in a web browser, the web browser will be redirected to http://sourceforge.net/projects/nagiosplug/files/ which contains the following statement:

“The Nagios Plugins are no longer distributed via SourceForge. For downloads and other information, please visit: https://www.nagios-plugins.org/
Source: README.md, updated 2013-10-01″

If you follow that link and then click the Download heading at the top of the page, there should be a link on the page that allows access to the current version of the Nagios plugins.  That link is currently: https://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz

The command that GabrielM provided should work:

wget https://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz

DiskStation DS212+ Notes:

The following configure, make, and make install commands were used:

./configure --prefix=/opt --with-openssl=/usr/syno/bin/openssl --with-nagios-user=nagios --with-nagios-group=nagios --with-ping-command="/opt/bin/ping -c %d %s" --psdir=/bin --with-ps-varlist="&procpid,&procppid,&procvsz,&procrss,procprog,&pos" --with-ps-cols=6 --with-ps-format="%d %d %d %d %s %n" --with-ps-command="/bin/ps -w"
make
make install

For the DiskStation DS1813+ 64 bit:

./configure --with-openssl=/usr/syno/bin/openssl --with-nagios-user=nagios --with-nagios-group=nagios --with-ping-command="/opt/bin/ping -c %d %s" --psdir=/bin --with-ps-varlist="&procpid,&procppid,&procvsz,&procrss,procprog,&pos" --with-ps-cols=6 --with-ps-format="%d %d %d %d %s %n" --with-ps-command="/bin/ps -w" --host=x86_64-linux-gnu --target=x86_64-linux-gnu --build=x86_64-pc-linux
make -i 
make install -i

For the DiskStation DS412+ 32 bit:

./configure --with-openssl=/usr/syno/bin/openssl --with-nagios-user=nagios --with-nagios-group=nagios --with-ping-command="/opt/bin/ping -c %d %s" --psdir=/bin --with-ps-varlist="&procpid,&procppid,&procvsz,&procrss,procprog,&pos" --with-ps-cols=6 --with-ps-format="%d %d %d %d %s %n" --with-ps-command="/bin/ps -w" --host=i686-linux-gnu --target=i686-linux-gnu --build=i686-linux-gnu --prefix=/usr/local
make -i 
make install -i

Copy the Nagios plugins to the location expected by Nagios:

DiskStation DS212+ Notes:

The plugins were installed in the correct location on the DS212+

cp /usr/local/nagios/libexec/*.* /opt/libexec
cp /usr/local/nagios/libexec/* /opt/libexec
cp /usr/local/libexec/check_* /opt/libexec

Update November 19, 2013:

GabrielM reported in a comment below that the third command above may fail.  Depending on the compile options used, the first two commands or the third command may fail.  The first two commands are intended to accomplish the same task as the third command; the first two commands or the last command are expected to fail, but all three commands should not fail.  I should have explained this potential area of concern better.

Copy the Nagios startup script to the correct location so that Nagios will automatically start when the DiskStation is rebooted:

cp /usr/local/etc/rc.d/nagios /opt/etc/init.d/S81nagios

Verify that the ownership of the nagios directory is set correctly:

DiskStation DS212+ Notes:

The file is actually in the /opt/bin directory, so use this command instead:

chown nagios:nagios /opt/bin/nagios/nagios -R
chown nagios:nagios /usr/local/nagios -R

In addition to the main /opt/etc/nagios.cfg Nagios file, there are several other configuration files that are potentially used by Nagios (defined in the nagios.cfg file):

/opt/etc/objects/commands.cfg
/opt/etc/objects/contacts.cfg
/opt/etc/objects/timeperiods.cfg
/opt/etc/objects/templates.cfg
/opt/etc/objects/localhost.cfg
/opt/etc/objects/windows.cfg
/opt/etc/objects/server.cfg
/opt/etc/objects/switch.cfg
/opt/etc/objects/printer.cfg

We need to make a couple of adjustments in the  /opt/etc/objects/commands.cfg file.

vi /opt/etc/objects/commands.cfg

Change the ‘notify-host-by-email’ command definition section as follows:

define command{
    command_name notify-host-by-email
    command_line /usr/bin/printf "%b" "Subject: $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /opt/sbin/sendmail -vt $CONTACTEMAIL$
    }

Change the ‘notify-service-by-email’ command definition section as follows:

define command{
    command_name notify-service-by-email
    command_line /usr/bin/printf "%b" "Subject: $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$\n" | /opt/sbin/sendmail -vt $CONTACTEMAIL$
    }

Change the ‘check_ping’ command definition section as follows (feel free to read the documentation for check_ping and specify different values):

define command{
        command_name    check_ping
        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000,25% -c 5000,90% -p 3 
        }

Save the file and exit vi.

At this point, the Nagios network monitoring utility will likely experience an error similar to the following when attempting to send an alert email:

output=collect: Cannot write ./dfr6BFFPC7027203 (bfcommit, uid=1026, gid=25): Permission denied

Execute the following commands, which should fix the above problem:

chmod g+w /opt/var/spool/clientmqueue
chmod 444 /opt/etc/mail/*.cf
chmod 7555 /opt/sbin/sendmail

We will need to use su to test the execution of various commands as the nagios user.  Without this fix (described here), you might see the following error message:

su: warning: cannot change directory to /var/services/homes/nagios: No such file or directory su: /sbin/nologin: No such file or directory

Enter the following commands:

mkdir /var/services/homes
mkdir /var/services/homes/nagios
chown nagios:nagios /var/services/homes/nagios -R
vi /etc/passwd

Locate the line in the passwd file for the Nagios user.  Near the end of the line, /sbin/nologin should appear.  Replace that text with /bin/ash then save and exit vi.

Verify that the Nagios user is able to execute the check_ping plugin.  Replace MyDeviceHere with either an IP address or a network device name that is on your network:

su - nagios -c "/opt/libexec/check_ping -H MyDeviceHere -w 5000,80% -c 5000,80% -p 5"

If the ping command (called by check_ping) is not able to resolve a network device name, and the fully qualified dns name was not specified (MyDeviceHere.MyDomainHere.com), edit the /etc/resolv.conf file:

vi /etc/resolv.conf

On a new line in the file, add the following line (replacing MyDomainHere.com with your dns domain name for the network):

search MyDomainHere.com

Verify that sendmail works for the Nagios user.  At the prompt that appears, type a short message, press the Enter key, type a period, then press the Enter key again – replace MyEmailAddressHere@MyDomainHere.com with your email address):

su - nagios -c "/opt/sbin/sendmail -vt MyEmailAddressHere@MyDomainHere.com"

—-

It is important to always verify the Nagios configuration before starting (or restarting after a configuration change) Nagios.  To verify the configuration type the following:

/opt/bin/nagios -v /opt/etc/nagios.cfg

To start up Nagios as a background task (daemon), execute the following:

/opt/bin/nagios -d /opt/etc/nagios.cfg

To stop Nagios that is executing as a background task, type:

ps

InstallNagiosDiskStation11

Then search though the list of processes for the first line that shows /opt/bin/nagios -d /opt/etc/nagios.cfg.  The number at the left of that line, 31152 in this case, is used to stop Nagios.  To stop Nagios, type the following (replace 31152 with the number shown on your screen):

kill 31152

Side note: I tried installing quite a few different C++ compilers that supposedly work with the Synology DSM (see here).  As such, I had to find a way to remove a directory, that directory’s subdirectories, and files.  The following command will completely remove the /usr/local/i686-linux-gnu directory, should the need arise:

rm -rf /usr/local/i686-linux-gnu

At this point, Nagios will hopefully run as a background task, and it should be able to ping and send email alerts.  However, if you were following the above directions, we have not yet instructed Nagios which devices to monitor, and to whom the alert emails should be sent.  The next step is to define the email contacts by modifying the /opt/etc/objects/contacts.cfg file (see the documentation for assistance):

vi /opt/etc/objects/contacts.cfg

After setting up the contacts, we should probably tell Nagios which devices to monitor.  If there are a lot of devices on your network to be monitored, you might find that using Microsoft Excel rather than vi to create the object definitions makes the task more manageable.  Set up a simple worksheet with four columns.  Column A will be used to specify the short host_name for the object to be monitored.  Column B will be used to specify the alias (long description for the object).  Column C will be used to either specify the IP address for the device or the network name for the device.  Column D will be used to identify the group to which the object belongs and the file name to which the definition is saved (the Excel macro supports the following groups: ap, camera, computer, external, other, printer, server, switch).

InstallNagiosDiskStation13

The Excel macro is set up to read a tab delimited file, rather than reading the object description directly from the Excel worksheet.  Highlight all of the rows in the worksheet except for the top header row, and press Ctrl C (or edit – Copy) to copy the definitions to the Windows clipboard in tab delimited format.  Start Notepad (Start – Run – Notepad), and then press Ctrl V (or edit – Paste) to paste the tab delimited object descriptions into Notepad.  The Excel macro code expects the text file to be saved as nagioshosts.txt.

The Excel macro code follows (I image that not many computers still have a second floppy drive installed, so change the B:\Hardware Documentation\Synology\ path as appropriate for your environment):

Private Sub cmdProcessText_Click()
    Dim intFileNumRead As Integer
    Dim intFileNumAP As Integer
    Dim intFileNumCamera As Integer
    Dim intFileNumComputer As Integer
    Dim intFileNumExternal As Integer
    Dim intFileNumOther As Integer
    Dim intFileNumPrinter As Integer
    Dim intFileNumServer As Integer
    Dim intFileNumSwitch As Integer
    Dim intFileNumWrite As Integer

    Dim strLine As String
    Dim strItem() As String

    intFileNumRead = FreeFile
    Open "B:\Hardware Documentation\Synology\nagioshosts.txt" For Input As #intFileNumRead

    intFileNumAP = FreeFile
    Open "B:\Hardware Documentation\Synology\ap.cfg" For Output As intFileNumAP
    Print #intFileNumAP, "###############################################################################"; Chr(10);
    Print #intFileNumAP, "# ap.cfg - lists the wireless access points to be monitored"; Chr(10);
    Print #intFileNumAP, "#"; Chr(10);
    Print #intFileNumAP, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumAP, "###############################################################################"; Chr(10);
    Print #intFileNumAP, "#"; Chr(10); Chr(10);
    Print #intFileNumAP, "###############################################################################"; Chr(10);
    Print #intFileNumAP, "#"; Chr(10);
    Print #intFileNumAP, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumAP, "#"; Chr(10);
    Print #intFileNumAP, "###############################################################################"; Chr(10);
    Print #intFileNumAP, "#"; Chr(10); Chr(10);
    Print #intFileNumAP, "define hostgroup{"; Chr(10);
    Print #intFileNumAP, "        hostgroup_name  ap                      ; The name of the hostgroup"; Chr(10);
    Print #intFileNumAP, "        alias           Local Access Points       ; Long name of the group"; Chr(10);
    Print #intFileNumAP, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumCamera = FreeFile
    Open "B:\Hardware Documentation\Synology\camera.cfg" For Output As intFileNumCamera
    Print #intFileNumCamera, "###############################################################################"; Chr(10);
    Print #intFileNumCamera, "# camera.cfg - lists the IP cameras to be monitored"; Chr(10);
    Print #intFileNumCamera, "#"; Chr(10);
    Print #intFileNumCamera, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumCamera, "###############################################################################"; Chr(10);
    Print #intFileNumCamera, "#"; Chr(10); Chr(10);
    Print #intFileNumCamera, "###############################################################################"; Chr(10);
    Print #intFileNumCamera, "#"; Chr(10);
    Print #intFileNumCamera, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumCamera, "#"; Chr(10);
    Print #intFileNumCamera, "###############################################################################"; Chr(10);
    Print #intFileNumCamera, "#"; Chr(10); Chr(10);
    Print #intFileNumCamera, "define hostgroup{"; Chr(10);
    Print #intFileNumCamera, "        hostgroup_name  camera                  ; The name of the hostgroup"; Chr(10);
    Print #intFileNumCamera, "        alias           Local IP Cameras          ; Long name of the group"; Chr(10);
    Print #intFileNumCamera, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumComputer = FreeFile
    Open "B:\Hardware Documentation\Synology\computer.cfg" For Output As intFileNumComputer
    Print #intFileNumComputer, "###############################################################################"; Chr(10);
    Print #intFileNumComputer, "# computer.cfg - lists the shop floor computers to be monitored"; Chr(10);
    Print #intFileNumComputer, "#"; Chr(10);
    Print #intFileNumComputer, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumComputer, "###############################################################################"; Chr(10);
    Print #intFileNumComputer, "#"; Chr(10); Chr(10);
    Print #intFileNumComputer, "###############################################################################"; Chr(10);
    Print #intFileNumComputer, "#"; Chr(10);
    Print #intFileNumComputer, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumComputer, "#"; Chr(10);
    Print #intFileNumComputer, "###############################################################################"; Chr(10);
    Print #intFileNumComputer, "#"; Chr(10); Chr(10);
    Print #intFileNumComputer, "define hostgroup{"; Chr(10);
    Print #intFileNumComputer, "        hostgroup_name  computer               ; The name of the hostgroup"; Chr(10);
    Print #intFileNumComputer, "        alias           Domain Computers          ; Long name of the group"; Chr(10);
    Print #intFileNumComputer, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumExternal = FreeFile
    Open "B:\Hardware Documentation\Synology\external.cfg" For Output As intFileNumExternal
    Print #intFileNumExternal, "###############################################################################"; Chr(10);
    Print #intFileNumExternal, "# external.cfg - lists the devices external to the LAN network to be monitored"; Chr(10);
    Print #intFileNumExternal, "#"; Chr(10);
    Print #intFileNumExternal, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumExternal, "###############################################################################"; Chr(10);
    Print #intFileNumExternal, "#"; Chr(10); Chr(10);
    Print #intFileNumExternal, "###############################################################################"; Chr(10);
    Print #intFileNumExternal, "#"; Chr(10);
    Print #intFileNumExternal, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumExternal, "#"; Chr(10);
    Print #intFileNumExternal, "###############################################################################"; Chr(10);
    Print #intFileNumExternal, "#"; Chr(10); Chr(10);
    Print #intFileNumExternal, "define hostgroup{"; Chr(10);
    Print #intFileNumExternal, "        hostgroup_name  external               ; The name of the hostgroup"; Chr(10);
    Print #intFileNumExternal, "        alias           Monitored devices External to the Network ; Long name of the group"; Chr(10);
    Print #intFileNumExternal, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumOther = FreeFile
    Open "B:\Hardware Documentation\Synology\other.cfg" For Output As intFileNumOther
    Print #intFileNumOther, "###############################################################################"; Chr(10);
    Print #intFileNumOther, "# other.cfg - lists the miscellaneous devices to be monitored"; Chr(10);
    Print #intFileNumOther, "#"; Chr(10);
    Print #intFileNumOther, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumOther, "###############################################################################"; Chr(10);
    Print #intFileNumOther, "#"; Chr(10); Chr(10);
    Print #intFileNumOther, "###############################################################################"; Chr(10);
    Print #intFileNumOther, "#"; Chr(10);
    Print #intFileNumOther, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumOther, "#"; Chr(10);
    Print #intFileNumOther, "###############################################################################"; Chr(10);
    Print #intFileNumOther, "#"; Chr(10); Chr(10);
    Print #intFileNumOther, "define hostgroup{"; Chr(10);
    Print #intFileNumOther, "        hostgroup_name  other                 ; The name of the hostgroup"; Chr(10);
    Print #intFileNumOther, "        alias           Miscellaneous Devices ; Long name of the group"; Chr(10);
    Print #intFileNumOther, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumPrinter = FreeFile
    Open "B:\Hardware Documentation\Synology\printer.cfg" For Output As intFileNumPrinter
    Print #intFileNumPrinter, "###############################################################################"; Chr(10);
    Print #intFileNumPrinter, "# printer.cfg - lists the printer devices to be monitored"; Chr(10);
    Print #intFileNumPrinter, "#"; Chr(10);
    Print #intFileNumPrinter, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumPrinter, "###############################################################################"; Chr(10);
    Print #intFileNumPrinter, "#"; Chr(10); Chr(10);
    Print #intFileNumPrinter, "###############################################################################"; Chr(10);
    Print #intFileNumPrinter, "#"; Chr(10);
    Print #intFileNumPrinter, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumPrinter, "#"; Chr(10);
    Print #intFileNumPrinter, "###############################################################################"; Chr(10);
    Print #intFileNumPrinter, "#"; Chr(10); Chr(10);
    Print #intFileNumPrinter, "define hostgroup{"; Chr(10);
    Print #intFileNumPrinter, "        hostgroup_name  printer               ; The name of the hostgroup"; Chr(10);
    Print #intFileNumPrinter, "        alias           Printers and Copiers  ; Long name of the group"; Chr(10);
    Print #intFileNumPrinter, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumServer = FreeFile
    Open "B:\Hardware Documentation\Synology\server.cfg" For Output As intFileNumServer
    Print #intFileNumServer, "###############################################################################"; Chr(10);
    Print #intFileNumServer, "# server.cfg - lists the servers to be monitored"; Chr(10);
    Print #intFileNumServer, "#"; Chr(10);
    Print #intFileNumServer, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumServer, "###############################################################################"; Chr(10);
    Print #intFileNumServer, "#"; Chr(10); Chr(10);
    Print #intFileNumServer, "###############################################################################"; Chr(10);
    Print #intFileNumServer, "#"; Chr(10);
    Print #intFileNumServer, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumServer, "#"; Chr(10);
    Print #intFileNumServer, "###############################################################################"; Chr(10);
    Print #intFileNumServer, "#"; Chr(10); Chr(10);
    Print #intFileNumServer, "define hostgroup{"; Chr(10);
    Print #intFileNumServer, "        hostgroup_name  server               ; The name of the hostgroup"; Chr(10);
    Print #intFileNumServer, "        alias           Server and Similar Devices ; Long name of the group"; Chr(10);
    Print #intFileNumServer, "        }"; Chr(10); Chr(10); Chr(10);

    intFileNumSwitch = FreeFile
    Open "B:\Hardware Documentation\Synology\switch.cfg" For Output As intFileNumSwitch
    Print #intFileNumSwitch, "###############################################################################"; Chr(10);
    Print #intFileNumSwitch, "# switch.cfg - lists the network equipment type devices to be monitored"; Chr(10);
    Print #intFileNumSwitch, "#"; Chr(10);
    Print #intFileNumSwitch, "# Last Modified: "; Now; Chr(10);
    Print #intFileNumSwitch, "###############################################################################"; Chr(10);
    Print #intFileNumSwitch, "#"; Chr(10); Chr(10);
    Print #intFileNumSwitch, "###############################################################################"; Chr(10);
    Print #intFileNumSwitch, "#"; Chr(10);
    Print #intFileNumSwitch, "# HOST GROUP DEFINITIONS"; Chr(10);
    Print #intFileNumSwitch, "#"; Chr(10);
    Print #intFileNumSwitch, "###############################################################################"; Chr(10);
    Print #intFileNumSwitch, "#"; Chr(10); Chr(10);
    Print #intFileNumSwitch, "define hostgroup{"; Chr(10);
    Print #intFileNumSwitch, "        hostgroup_name  switch               ; The name of the hostgroup"; Chr(10);
    Print #intFileNumSwitch, "        alias           Switche and Similar Devices ; Long name of the group"; Chr(10);
    Print #intFileNumSwitch, "        }"; Chr(10); Chr(10); Chr(10);

    Do While Not (EOF(intFileNumRead))
        Line Input #intFileNumRead, strLine
        strItem = Split(strLine, vbTab)
        'strItem(0) = host_name
        'strItem(1) = alias
        'strItem(2) = address
        'strItem(3) = hostgroups
        Select Case strItem(3)
            Case "ap"
                intFileNumWrite = intFileNumAP
            Case "camera"
                intFileNumWrite = intFileNumCamera
            Case "computer"
                intFileNumWrite = intFileNumComputer
            Case "external"
                intFileNumWrite = intFileNumExternal
            Case "other"
                intFileNumWrite = intFileNumOther
            Case "printer"
                intFileNumWrite = intFileNumPrinter
            Case "server"
                intFileNumWrite = intFileNumServer
            Case "switch"
                intFileNumWrite = intFileNumSwitch
        End Select

        Print #intFileNumWrite, "define host{"; Chr(10);
        Select Case strItem(3)
            Case "ap"
                Print #intFileNumWrite, "        use             ap              ; Inherit default values from a template"; Chr(10);
            Case "camera"
                Print #intFileNumWrite, "        use             camera          ; Inherit default values from a template"; Chr(10);
            Case "computer"
                Print #intFileNumWrite, "        use             computer        ; Inherit default values from a template"; Chr(10);
            Case "external"
                Print #intFileNumWrite, "        use             external        ; Inherit default values from a template"; Chr(10);
            Case "other"
                Print #intFileNumWrite, "        use             other           ; Inherit default values from a template"; Chr(10);
            Case "printer"
                Print #intFileNumWrite, "        use             printer         ; Inherit default values from a template"; Chr(10);
            Case "server"
                Print #intFileNumWrite, "        use             server          ; Inherit default values from a template"; Chr(10);
            Case "switch"
                Print #intFileNumWrite, "        use             switch          ; Inherit default values from a template"; Chr(10);
        End Select
        Print #intFileNumWrite, "        host_name       "; strItem(0); "         ; The name we're giving to this device"; Chr(10);
        Print #intFileNumWrite, "        alias           "; strItem(1); "         ; A longer name associated with the device"; Chr(10);
        Print #intFileNumWrite, "        address         "; strItem(2); "         ; IP address of the device"; Chr(10);
        Print #intFileNumWrite, "        hostgroups      "; strItem(3); "         ; Host groups this device is associated with"; Chr(10);
        Print #intFileNumWrite, "        }"; Chr(10); Chr(10);

        Print #intFileNumWrite, "define service{"; Chr(10);
        Print #intFileNumWrite, "        use                     generic-service ; Inherit values from a template"; Chr(10);
        Print #intFileNumWrite, "        host_name               "; strItem(0); "        ; The name of the host the service is associated with"; Chr(10);
        Print #intFileNumWrite, "        service_description     PING            ; The service description"; Chr(10);
        Print #intFileNumWrite, "        check_command           check_ping!3000,25%!5000,90%    ; The command used to monitor the service"; Chr(10);
        Print #intFileNumWrite, "        normal_check_interval   5               ; Check the service every 5 minutes under normal conditions"; Chr(10);
        Print #intFileNumWrite, "        retry_check_interval    1               ; Re-check the service every minute until its final/hard state is determined"; Chr(10);
        Print #intFileNumWrite, "        }"; Chr(10); Chr(10);
    Loop

    Close #intFileNumRead
    Close #intFileNumAP
    Close #intFileNumCamera
    Close #intFileNumComputer
    Close #intFileNumExternal
    Close #intFileNumOther
    Close #intFileNumPrinter
    Close #intFileNumServer
    Close #intFileNumSwitch
End Sub

The files that are created use Unix/Linux standard line feed end of line marker characters, rather than the Windows standard carriage return/line feed combination characters.  As such, opening the generated files using Notepad is not advised.  Copy the generated files back to the /opt/etc/objects/ path on the DiskStation (copy the files to a Shared Folder on the DiskStation, then use the cp command to copy the files from the share location to /opt/etc/objects/ – the Shared Folders are typically created as a subdirectory in the /volume1/ directory).

If you decided to use some of the non-standard Nagios group names (as I did), those non-standard group names must be defined in the /opt/etc/objects/templates.cfg file:

vi /opt/etc/objects/templates.cfg

A portion of the additional entries that I made in this file include the following:

define host{
       name                    ap      ; The name of this host template
       use                     generic-host    ; Inherit default values from the generic-host temp
       check_period            24x7            ; By default, access points are monitored round t
       check_interval          5               ; Actively check the access point every 5 minutes
       retry_interval          1               ; Schedule host check retries at 1 minute intervals
       max_check_attempts      10              ; Check each access point 10 times (max)
       check_command           check_ping      ; Default command to check if access points are "alive"
       notification_period     24x7            ; Send notification out at any time - day or night
       notification_interval   30              ; Resend notifications every 30 minutes
       notification_options    d,r             ; Only send notifications for specific host states
       contact_groups          admins          ; Notifications get sent to the admins by default
       hostgroups              ap ; Host groups that access points should be a member of
       register                0               ; DONT REGISTER THIS - ITS JUST A TEMPLATE
       }

define host{
       name                    camera  ; The name of this host template
       use                     generic-host    ; Inherit default values from the generic-host temp
       check_period            24x7            ; By default, cameras are monitored round t
       check_interval          60              ; Actively check the device every 60 minutes
       retry_interval          1               ; Schedule host check retries at 1 minute intervals
       max_check_attempts      10              ; Check each device 10 times (max)
       check_command           check_ping      ; Default command to check if device are "alive"
       notification_period     24x7            ; Send notification out at any time - day or night
       notification_interval   240             ; Resend notifications every 240 minutes
       notification_options    d,r             ; Only send notifications for specific host states
       contact_groups          admins          ; Notifications get sent to the admins by default
       hostgroups              camera ; Host groups that cameras should be a member of
       register                0               ; DONT REGISTER THIS - ITS JUST A TEMPLATE
       }

Nagios will not know that it should read the additional configuration files until it is told to do so by modifying the /opt/etc/nagios.cfg file.

vi /opt/etc/nagios.cfg

Add the following lines to the nagios.cfg file:

# Charles Hooper's object types
cfg_file=/opt/etc/objects/ap.cfg
cfg_file=/opt/etc/objects/camera.cfg
cfg_file=/opt/etc/objects/computer.cfg
cfg_file=/opt/etc/objects/external.cfg
cfg_file=/opt/etc/objects/other.cfg
cfg_file=/opt/etc/objects/printer.cfg
cfg_file=/opt/etc/objects/server.cfg
cfg_file=/opt/etc/objects/switch.cfg

We have made a large number of changes to the configuration files, so it is important to verify that there are no errors in the configuration:

/opt/bin/nagios -v /opt/etc/nagios.cfg

If no errors are found in the configuration, terminate (kill) nagios and then restart as described above.

—-

Update July 28, 2013:

When attempting to start Nagios in daemon mode (/opt/bin/nagios -d /opt/etc/nagios.cfg) I encountered a couple of problems related to permissions for the Nagios user.  The nagios process was not listed when I used the ps command.  I then tried executing the following commands:

touch /opt/var/nagios.log
chown nagios:nagios /opt/var/nagios.log

Nagios was then able to start in daemon mode, but wrote messages similar to the following in the /opt/var/nagios.log file:

[1375058364] Warning: Could not open object cache file ‘/opt/var/objects.cache’ for writing!
[1375058364] Failed to obtain lock on file /opt/var/nagios.lock: Permission denied
[1375058364] Bailing out due to errors encountered while attempting to daemonize… (PID=11451)
[1375058656] Nagios 3.5.0 starting… (PID=12936)
[1375058656] Local time is Sun Jul 28 20:44:16 EDT 2013
[1375058656] LOG VERSION: 2.0
[1375058656] Warning: Could not open object cache file ‘/opt/var/objects.cache’ for writing!
[1375058656] Failed to obtain lock on file /opt/var/nagios.lock: Permission denied
[1375058656] Bailing out due to errors encountered while attempting to daemonize… (PID=12936)
[1375060107] Error: Unable to create temp file for writing status data: Permission denied
[1375060117] Error: Unable to create temp file for writing status data: Permission denied
[1375060127] Error: Unable to create temp file for writing status data: Permission denied
[1375060137] Error: Unable to create temp file for writing status data: Permission denied
[1375060147] Error: Unable to create temp file for writing status data: Permission denied
[1375060157] Error: Unable to create temp file for writing status data: Permission denied

I tried to set the permissions for a couple of other files, only to find another long list of Permission denied messages:

touch /opt/var/objects.cache
touch /opt/var/nagios.lock
touch /opt/var/nagios.tmp
chown nagios:nagios /opt/var/objects.cache
chown nagios:nagios /opt/var/nagios.lock
chown nagios:nagios /opt/var/nagios.tmp

I then recalled that I had seen similar messages on the DiskStation DS412+.  I then tried a different approach, creating a nagios directory in the /opt/var directory, creating a couple of subdirectories in that directory, and then assigning nagios as the owner of that directory structure:

mkdir /opt/var/nagios
mkdir /opt/var/nagios/archives
mkdir /opt/var/nagios/spool
mkdir /opt/var/nagios/spool/checkresults
chown nagios:nagios /opt/var/nagios -R
vi /opt/etc/nagios.cfg

In the nagios.cfg file, I made the following changes:

log_file=/opt/var/nagios/nagios.log
status_file=/opt/var/nagios/status.dat
lock_file=/opt/var/nagios/nagios.lock
temp_file=/opt/var/nagios/nagios.tmp
log_archive_path=/opt/var/nagios/archives
check_result_path=/opt/var/nagios/spool/checkresults
state_retention_file=/opt/var/nagios/retention.dat
debug_file=/opt/var/nagios/nagios.debug

After saving the file and exiting vi, I restarted Nagios in daemon mode.  Reading the last 100 lines of the Nagios log file is now accomplished with this command:

tail -n 100 /opt/var/nagios/nagios.log

—-

There are a lot of seemingly interesting Nagios plugins, including check_oracle (I believe that this plugin requires the Oracle client to be installed – good luck with that install).  On one of the DiskStations the check_snmp plugin did not compile, while on the other DiskStation the check_http plugin did not compile.

It might be interesting to see what solutions readers are able to develop from the above starting point.  The above information is the result of many hours of experimentation as well as a couple minutes reading through sections of the Nagios documentation (it reads like the Oracle Database documentation, so it should be an easy read once I am in the right mood) and hopelessly scanning the ‘net for information about obscure error messages.  Have fun, and try not to put the DiskStation out of service due to a mistaken file copy.

Update November 19, 2013:

Installing an updated version of the Synology DSM operating system may temporarily disable Nagios.  Make backups of all Nagios confirguration files (copying the files with the cp command to a directory in /volume1 is generally safe) before installing different versions of the Synology DSM operating system.

The DSM 4.3 operating system installation apparently removed the /var/services/homes directory.  That directory removal makes it impossible for the Nagios user to login to run various commands.  I assume that the removal of the homes directory is intentional, so a work around for that problem:

mkdir /var/services/home
mkdir /var/services/home/nagios
chown nagios:nagios /var/services/home/nagios -R
vi /etc/passwd

In the /etc/passwd file, change all /homes/ entries to /home/ then save and exit vi.

The installation of the different DSM version (including versions before 4.3) will likely also replace/remove the libltdl.* files located in /opt/local/lib and /usr/lib, so we need to copy those files back into the correct directories:

cp /opt/lib/libltdl.so.3 /opt/local/lib/libltdl.so.3
cp /opt/lib/libltdl.so.3 /usr/lib/libltdl.so.3
cp /opt/lib/libltdl.so /usr/lib/

Once the above items are copied, try executing the check_ping command as the nagios user (replace MyDeviceHere with either an IP address or the name of a device on your network).

su - nagios -c "/opt/libexec/check_ping -H MyDeviceHere -w 5000,80% -c 5000,80% -p 5"

If the DiskStation reports that the check_ping command was not found, then copy that file back to the /opt/libexec/ directory.  If the above command was successful, try verifying the Nagios configuration:

/opt/bin/nagios -v /opt/etc/nagios.cfg

If the verification was successful, start Nagios as a daemon:

/opt/bin/nagios -d /opt/etc/nagios.cfg

Execute the ps command and verify that the above command is listed in the running processes:

ps

Finally, verify that Nagios is still set to start automatically as a daemon:

ls /opt/etc/init.d/S81nagios

If a file is listed when the above command is executed, then Nagios should now be fully repaired.

Update August 11, 2015:

You may at some point need to verify that a http web server is online.  If you execute the following command (replace http://www.mydomain.com with an actual web server for a domain to be monitored):

/opt/libexec/check_http -H www.mydomain.com

You may see one of the following errors:

/opt/libexec/check_http: error while loading shared libraries: libssl.so.0.9.8: cannot open shared object file: No such file or directory
/opt/libexec/check_http: error while loading shared libraries: libcrypto.so.0.9.8: cannot open shared object file: No such file or directory

The problem is likely caused by two missing symbolic links.  The following commands worked on the DS415+ (and also both the DS1813+ and the DS412+):

ln -s /usr/lib/libssl.so.1.0.0 /usr/lib/libssl.so.0.9.8
ln -s /lib/libcrypto.so.1.0.0 /usr/lib/libcrypto.so.0.9.8




Review of Synology DS415+, How to Add Memory to the DS415+, Web Pages Not Databases

19 12 2014

December 19, 2014

As frequent readers of this blog likely know, I have not posted very many articles recently.  It seems that lately I struggle to find time to post interesting items to the blog, which is mostly focused on Oracle Database notes, so this is a three in one off-topic post.  I have been busy with a lot of items that are completely unrelated to Oracle Database.  One of those busy (non) work items is setting up a Synology DiskStation DS415+ NAS with four 6TB hard drives.

Part 1: Reviewing the Synology DS415+

Below is my review of that NAS, as posted on Amazon:


http://www.amazon.com/Synology-America-Station-Attached-DS415/dp/B00IKTSSIO/

I have previously purchased and implemented Synology Diskstation DS1813+, DS412+, DS214+, DS212+, DS213j, and DS112j units, so Synology network attached storage (NAS) devices are not entirely new to me (I also have experience with administering various Linux and Windows servers). Most of the Synology NAS units are configured primarily as FTP destinations, although the units also provide one or more Windows shares to network computers using either Active Directory integration or Synology Diskstation internal user accounts, as well as offering network time protocol (NTP) services (to security cameras, Active Directory, and/or a PBX system), and Nagios network monitoring.

For the most part, the Synology NAS units have been very reliable. That said I have experienced occasional problems with most of the NAS units that provide FTP services to security cameras. Eventually, all of the permitted client connections become “in use” due to the Synology sometimes remembering FTP connections long after the security cameras have forgotten about those connections. This connection “remembering” issue causes a situation where client computers attempting to connect for Windows file sharing are denied access to the server, but the problem also affects the web-based access to the Synology DSM operating system. There have been issues with the DiskStation DS412+ locking up roughly 90% of the time that a reboot is attempted through the web-based DSM, resulting in a blue flashing light on the front console that could only be fixed by pulling the electrical power cord (note that it is usually possible to kill phantom connections from the DSM interface, if that interface will display, so that a reboot is typically not required to recover from the “remembered” connections). None of the other DiskStations have experienced lockups during an attempted reboot (or any other lockups that I am able to recall).

The DS415+ was bought to take the place of a DS212+, whose CPU simply cannot keep pace with 15+ high definition security cameras feeding the NAS with motion triggered video clips via FTP. I had considered purchasing the new version of the DS1813+ (possibly called a DS1815+), but that model has not been released yet, probably would have the same Intel CPU model as the DS415+ (the DS1812+, 1813+, and DS412+ all have essentially the same CPU model), and likely would have had a higher electric wattage consumption compared to the DS415+ if I filled all drive bays. So, I selected the DS415+ as a device that had some known compromises, but with also some power efficiency benefits that are not present in the DS1813+ and DS412+.

The DS415+ ships with 2GB of memory in a regular memory slot, rather than being soldered to the system board as is the case for the DS412+, opening the possibility for future memory expansion. With two gigabit network ports, two USB 3 ports (one USB 2), and one eSATA port , the Synology DiskStation DS415+ offers decent storage expansion options, although those options are more limited than what is offered by the DS1813+. The DS415+ internally supports up to four hard drives in one of several software RAID levels (SHR, RAID 1, RAID 5, RAID 6, and RAID 10). Drive installs are potentially performed without using a screwdriver, although screws are provided to hold the drives in place if the screw-less arrangement seems too flimsy. Unlike the DS1813+, the drive carriages are held in place by a thumb-release locking clip, rather than a flimsy lock and key mechanism. The DiskStation DS415+ more than triples in weight with four typical hard drives installed – the light weight construction seems to be typical of the various Synology NAS units (at least those that support eight or fewer drives).

The DS415+ ships without an installed operating system, so the first task after powering on the DS415+ with the hard drives installed involves installing the latest DSM operating system. The process for installing the operating system is fairly simple, unless there is another DiskStation NAS on the same LAN (the directions provided in the printed quick start guide caused the DSM web page for another already set up Synology NAS to appear, rather than the operating system installation page for the DS415+ – the old Synology setup program that used to ship on CD with the NAS units probably would have helped in this situation). Once the NAS has nearly automatically downloaded the latest version of the operating system, the operating system installation should complete in a couple of minutes without a lot of issues.

The Synology DSM operating system offers a fantastic graphical user interface which implements HTML5 and CSS, displaying the interface in a web browser. Unfortunately, Synology tends to rearrange the location of various settings with each DSM version (and change the shape/color of icons), which makes it a little confusing when managing different Synology NAS units. Much like Windows Explorer, the File Station utility that is built into the DSM operating system supports context sensitive drag and drop, and well as right mouse button popup menus. The File Station utility that is included in the latest DSM version supports displaying more than 300 files in a paged view – that 300 file limit was an irritation when attempting to copy, move, or delete several thousand security camera videos on a daily basis through the GUI using older DSM versions. Like the other DSM models, the DS415+ supports telnet sessions, which allow access to the Linux command line and the configuration of scheduled script execution through the modification of the /etc/crontab file (side note: I have had issues with only the DS112j automatically resetting the contents of the /etc/crontab file when the DiskStation was power cycled – I believe that problem was caused by the use of spaces rather than tabs as field delimiters in the file).

A plain vanilla install of the DSM 5.0-4528 (as of today at update 1) offers support for network shares (Windows, MAC, and NFS), iSCSI, Active Directory Integration, FTP (standard FTP, anonymous FTP, FTPS, SFTP, TFTP), website hosting, WebDAV, SNMP, network time protocol (NTP), remote command line with telnet or SSH, integrated firewall, VPN client, USB printer sharing, and a handful of other capabilities. The DSM operating system’s native functionality is easily expanded through the download of free software packages from the Package Center. The packages extend the DS415+’s capabilities to include antivirus, Asterisk IP phone server, Internet radio rebroadcasting to networked computers, DNS server functionality, iTunes Server, VPN server, RADIUS server, email server, CRM and ERP packages, WordPress, IP camera monitoring (now includes a license for two IP cameras, additional licenses are roughly $50 per camera), and a variety of other features. Additionally, ipkg support permits the installation of more than 900 additional applications, including C++ compilers – which in theory suggests that the source for the Nagios network monitoring utility can be downloaded and compiled on the DS415+ (I was able to compile Nagios on a DS1813+, DS412+, and DS212+, and am close to having Nagios working on the DS415+).

I installed four new Western Digital Red 6TB drives, configured in a software RAID 10 array (DSM offered to automatically configure the drives in a SHR array during the initial setup, but did not offer a RAID 10 configuration at that time, so configuring the drives for RAID 10, to reduce recovery time in the event of a drive failure, requires a couple of additional mouse clicks). Peak single network link data transfer speeds so far have been impressive, at close to the maximum possible transfer rate for a gigabit network (achieving roughly 112-115MB/s ~ 919Mb/s), which is virtually identical to the speed seen with the DS1813+ that was using four 3TB Western Digital Red drives, and significantly faster than the DS212+ which has a much slower non-Intel CPU and two Western Digital Green 2TB drives. Pushing approximately 41.6GB of large files to the DS415+ from a client computer consumed between 9% and 11% of the DS415+’s CPU (for comparison, this test consumed 20% of the DS1813+ CPU capacity).

I did not test the DiskStation’s IEEE 802.3ad dynamic link aggregation – there was no apparent benefit when I tested the feature with the DS1813+, an HP 4208vl switch, and two client computers. The gigabit switch to which the DS415+ is attached does not support IEEE 802.3ad dynamic link aggregation, so it would have been a very bad idea to connect both of the supplied network cables to the switch.

Power Consumption of the DS415+(based on the output of a Kill-A-Watt meter):
* 1.1 watts when powered off
* 16 watts with no drives installed and unit is sitting idle
* 44 watts with four Western Digital Red 6TB drives while the unit is receiving files at a rate of 112-115MB/s (for comparison, this test required 46 watts with the DS1813+ when outfitted with four Western Digital Red 3TB drives)
* 39 watts with four Western Digital Red 6TB drives installed while the unit is sitting idle for a couple of minutes (identical to the value measured for the DS1813+)
* 14.5 watts with four Western Digital Red 6TB drives hibernating

Even though the throughput and CPU of the DS415+ with software based RAID are no match for the performance and capacity of a high end Windows or Linux server, the Synology NAS units consume far less electrical power, are competitively priced (even though these units are expensive once four 6TB drives are added), should yield a lower total cost of ownership (TCO), and are likely easier to configure and maintain for their intended purpose than either a Windows or Linux server. Like the DS1813+, the DS415+ supports up to 512 concurrent remote connections from other devices (a computer with five mapped drives pointing to the DS415+ consumes five of those 512 concurrent connections). The 512 connection count may not be the hard upper limit on the Synology NAS units – I have encountered some problems with the DS112J blocking connection attempts long before its 64 concurrent limit is reached – I do not yet know if this issue affects any of the other Synology device models. The lack of an available redundant power supply is a shortcoming of the DS1813+ and other less expensive Synology NAS units, but the power supply for the DS415+ (and the DS412+) is external, so it should be easier to obtain and install replacement power supplies for the DS415+ should the need arise (the power supply may not have a standardized connection, which would permit a replacement power supply to be purchased from a third party supplier).

Synology offers a group of customer support forums. However, those forums are apparently not actively monitored by Synology support staff. So far, other than whether or not Plex on the DS415+ is able to transcode 1080P videos, there has been no significant negative comments about the DS415+ on the Synology forums.

The Synology DiskStation DS212+ has served its role surprisingly well for the last two and a half years, even when equipped with slow Western Digital Green drives in a software RAID 1 array. While that NAS was able to support 15+ cameras that potentially simultaneously send video clips via FTP, concurrently allowing a Windows client to connect to the share for the purpose of reviewing the video clips was often just a bit too much of a load for the less powerful DS212+. I am expecting few problems from the DS415+ when serving in a similar role along with supporting a couple of optional packages such as the Media Server, Audio Station, Nagios (currently receiving a Segmentation fault (core dumped) error message when executing the check_ping test command found in my “Install Nagios on a Synology DiskStation DS1813+ or DS412+” blog article), and possibly Plex. Most of the optional Synology packages appear to be decent. However, the Synology Surveillance Station, while possibly useful, still seems to be an overly fragile, overly expensive, experimental package that tends to tax the wireless and wired network much more than the FTP solution that I use with my cameras (your experience with that package may be different than mine).


Part 2: Voiding the Warranty on the Synology DS415+ (Upgrading the Memory to 8GB)

The DS415+ ships with 2GB of DDR3 1600 MT/s (PC3-12800) CL11 SODIMM memory pre-installed, and from what I am able to determine, Synology does not and will not offer memory upgrades for the DS415+.  The memory is installed in a laptop style memory socket, so… I installed a Crucial 8GB memory card into the DS415+.  The greatest difficulty in the memory upgrade, other than the concern for destroying a $630 device, was breaking into the DS415+ case without destroying the plastic clips that hold the two halves of the case together.  I posted the upgrade process to one of the Synology forum threads, but I thought that I would also post the process in this blog article so that it is easier to find the steps for the process later (the pictures do not fully display in the Synology forum thread).

If you have never disassembled a desktop or laptop computer, consider just being happy with the installed 2GB of memory to avoid damaging the Synology. If you have never removed a memory card from a laptop, consider just being happy with the installed 2GB of memory to avoid damaging the Synology. Upgrading the memory will likely void the warranty – there was a label on the installed memory card indicating that the warranty was void if the label was removed from the memory – the label is still attached to my old memory card (so, maybe my warranty is still in effect 😉 ).

Step 1, unplug the Synology and attach labels to each of the hard drives. Write the numbers 1 through 4 on the labels to indicate the ordered position of the drives in the NAS. Release the latch, and remove the drives. Make certain that you ground yourself by touching a large metal object before attempting to open the NAS. When working on the NAS, do not touch any of the contacts inside the NAS or on the memory card (click for a larger view of the picture):

OLYMPUS DIGITAL CAMERA

There are three screws on the back of the Synology that will need to be removed using a #1 Phillips screw driver. Wait to remove the screw that is pointed to by the screw driver in this picture until you have successfully separated the two halves of the NAS case:

OLYMPUS DIGITAL CAMERA

There are plastic clips permanently attached to the smaller half of the NAS case. Use a flat blade screw driver to gently pry up on the larger section of the case at the top-back of the case near the seam between the two sections of the case. A popping sound should be heard when the latch releases. When the first latch releases, move the screw driver to the position of the next latch and gently pry up to again slightly raise the larger section of the case at the seam until the next latch releases. Continue working to release the remaining latches along the seam. Once all of the latches on the top are released, it should be possible to pivot the larger portion of the case so that the bottom latches release. Separate the two halves, being careful not to damage the retaining clips that normally hold the hard drives in place:

OLYMPUS DIGITAL CAMERA

There are four screws on each side of the drive cage – four of the screws are long, and four are short. Remove the screws using the #1 Phillips screw driver. Make note of where the longer screws were installed. Remove the third screw from the back of the NAS if it was not already removed:

OLYMPUS DIGITAL CAMERA

Gently lift the drive cage straight up and then set it aside. Note that there are two slots pointed to by the arrows in the picture below – when it is time to re-assemble the NAS, the gold colored ends of the two circuit boards must be reinserted into those slots. There are two fan connectors circled in the picture – if the fan connectors are carefully removed from the sockets, it is possible to move the drive cage out of the way. The circuit board holding the memory card is below the metal plate – that metal plate should lift straight out of the enclosure, although there may still be some wires that attach it to the enclosure

OLYMPUS DIGITAL CAMERA

There is a metal clip at each end of the pre-installed memory card. Gently push the left clip to the left, and the right clip to the right until the memory card releases. When removing the memory card, make note of the location of the cut out section of the slot, so that the replacement memory card may be installed in the same orientation:

OLYMPUS DIGITAL CAMERA

Slide the replacement memory card into the slot, and gently tilt it down until the two clips lock the memory card in location.

OLYMPUS DIGITAL CAMERA

Reverse the order of the steps to reassemble the NAS. Reinsert the drives in the correct order by referring to the labels. Plug in the NAS – the blue light on the front of the unit may flash for a minute or two.

If all goes well, the Resource Monitor in the Synology interface should show 8GB of memory installed:

dsm51-8gb

After a day or so, the NAS may show in Resource Monitor that it is using 6.9GB (or 7.0GB) of memory for the file cache, as shown below.

DSM51-8GB-2

Why install additional memory in the DS415+?  The 2GB of memory should be sufficient for most tasks that are typically assigned to a NAS.  I was mostly just curious after seeing a couple of questions on Amazon about memory upgrades, as well as on the Synology forums, without a clear description of the upgrade process, and only a passing mention of the memory specifications in a review of the DS415+.  There were a handful of discussion threads on the Synology forums were people were trying various memory modules in their DS1815+ units, and mostly failing to locate compatible memory modules (the Crucial memory module that I used was reported to not work in the DS1815+’s easily accessible memory slot).  So, I bought the memory, tried to figure out how to break into the DS415+ case, and took pictures as I put the unit back together (I thought that there was little point in me taking pictures while disassembling the NAS, especially if I destroyed the NAS during the upgrade attempt, but doing so while disassembling the unit is probably a good idea).

How does the DS415+ utilize the additional memory?  Mostly for the file cache (the NAS runs Linux at its core) – a day after the upgrade I checked the Resource Monitor and found that the Cached statistic increased from 670.5MB to roughly 7GB.  If there is an interest in running various background packages on the NAS (for instance, Nagios, Plex, WordPress, etc.), then the additional memory could have a significant positive impact on performance.  My installation directions for installing Nagios on a Synology DS412+, DS1813+, and DS212+ almost work with the DS415+.  I receive a Segmentation Fault, Core Dumped error message when trying to run Nagios or the Nagios check_ping plugin – I seem to recall seeing similar error messages when trying to find a compiler that would work on the DS412+ and DS1813+, so maybe there is still hope for Nagios on the DS415+ if I try downloading a different compiler (the ToolChain library for the DS415+ was released roughly a week ago, so there may be a solution – I was able to compile Nagios once and it executed without a Segmentation Fault error, but I could not reproduce the result a second time).

Part 3: Web Pages Not Databases

While I have had this blog on wordpress.com for a bit over five years, I had never tried using the standalone version of WordPress.  An opportunity developed recently to use the standalone version of WordPress.  The website for the company were I work has been in the process of being redesigned since roughly June by an outside web development company.  That web development company was making very slow progress on the website, selected to use the standalone version of WordPress as the development environment, and somehow was apparently given the task of designing the website so that it looked great on an Apple iPad, Apple iPhone, and even a now extinct Motorola Xoom tablet – any compatibility with Windows desktop computers using Internet Explorer seemed to be purely accidental, but the website apparently appeared fine on the developer’s Mac.  (Hint for developers: Test what you create using equipment that is similar to your target audience’s equipment.)

I became involved in the new website development a couple of weeks ago, trying to refocus the web development company on what the target viewers of the website will likely be using to view and interact with the new website – chances are that a too-large-to-ignore percentage of those target viewers are still running Windows Vista or Windows XP, and will be accessing the site using some version of Internet Explorer other than the latest version (some websites still are not compatible with Internet Explorer 11, so the potential target viewer may still be forced to run Internet Explorer 8 or 9 – Internet Explorer 8 is the last version supported on Windows XP, and Internet Explorer 10 is the last version supported on Windows Vista).  Ability to Print?  No, people have no need to print the website’s contents (especially not using a PDF virtual printer from Adobe or BlackIce, where all of the text from the new website was replaced by odd symbols) and have it appear on the printed page anything like what appears on-screen.  Viewing the website in a non-maximized window – who would be so silly to do such a thing?  Hamburgers are not on the menu – they are the menu, or it seems that is the official name for the three parallel white lines that sometimes appear on screen and sometimes in the printed copy.  Developers are full of fun surprises some days.

A week ago (now two weeks ago) the web development company was told to stop development on the website for a variety of reasons.  A lost six months of development, or an opportunity to beat one’s head on the table and hammer out a solution for the issues that still existed with the website?  I installed the WordPress package on a Synology DS213j NAS and on the Synology DS415+ NAS and had a go at fixing the issues with the website without affecting what the web development company had done to date.  I picked up the development process reasonably quickly (the five years of blogging on WordPress helped), but found that I was repeatedly flipping back and forth between WordPress’ Visual editor and the Text editor while trying to fix the issues and add additional text information to the pages.  Additionally, the path to files (and web pages) on the Synology must also include /wordpress (when working in a Telnet session, the actual path is /volume1/web/wordpress).  My HTML and cascading style sheet (CSS) skills were very rusty, so tasks that are incredibly easy in Microsoft Excel and Microsoft Word, such as manipulating tables, took quite a bit of Google search skill, for instance determing how to right align or center certain columns in a table without altering the HTML TD attributes of each table cell in the column when composing the table using the WordPress Text editor.  The WordPress pages appeared acceptable on the Synology NAS units, so the same changes were applied to the web development company’s best efforts – hopefully this is not true, but I think that I made about as much progress on the website in three days time as did the web development company in the last three months.  Since then I have been fixing other minor issues, such as the search box disappearing when the website is viewed using a Windows 8.1 tablet that lacks a keyboard and mouse, and improving the website appearance.  I learned, or relearned a couple of skills along the way, so this process definitely was not a wasted effort.

The VP of Manufacturing at the company where I work has become somewhat of an expert recently at creating video and composing written content for the website, so the newly redesigned website is a good platform for his many years of manufacturing experience.  If you feel so inclined, take a look at the new company website, and leave a message here to let me know what you think about the website.  The website development company didn’t like my green highlight when the mouse pointer passed over links – I guess that explains one reason why I am not a graphics artist.

For future reference, this is the CSS code that I constructed to format some of the tables that appear on the website.  In the HTML code, I assigned the table to have a class of km_equipment_table, and then added the following to the style sheet’s CSS:

Handle general formatting of the table:

.km_equipment_table { width:100%; border:1px solid; padding:8px;  }
.km_equipment_table td { padding:8px; border:1px solid; }
.km_equipment_table th {background: #0072BC;}

Alternate between two colors for all but the header row of the table – note that this code is ignored by Internet Explorer 8.0:

.km_equipment_table tr:nth-child(even) { /*(even) or (2n 0)*/
 background: #F1F1F1; border:1px solid;
}
.km_equipment_table.tr:nth-child(odd) { /*(odd) or (2n 1)*/
 background: #FFFFFF; border:1px solid;
}

Set the column alignment of all tables that were assigned the class of km_equipment_table – the first column is number 1 (not 0) – note that this code is ignored by Internet Explorer 8.0:

.km_equipment_table td:nth-child(1) {
    text-align: left;
}
.km_equipment_table td:nth-child(2) {
    text-align: center;
}
.km_equipment_table td:nth-child(3) {
    text-align: right;
}
.km_equipment_table td:nth-child(4) {
    text-align: right;
}
.km_equipment_table td:nth-child(5) {
    text-align: right;
}
.km_equipment_table td:nth-child(6) {
    text-align: right;
}
.km_equipment_table td:nth-child(7) {
    text-align: right;
}

To keep the printed copy of the page appearing correct, I had to specify @media screen for several of the style sheets.  As such, a special style sheet, print.css, was previously set up to handle formatting when printing.  Among other adjustments in that print.css style sheet, I added the following so that the column alignment worked correctly in the printed copy of the web pages (note that this code did not work on Internet Explorer 8.0):

.km_equipment_table {
    border:solid #000 !important;
    border-width:1px 0 0 1px !important;
}
.km_equipment_table.th, .km_equipment_table.td {
    border:solid #000 !important;
    border-width:0 1px 1px 0 !important;
}
.km_equipment_table td:nth-child(1) {
    text-align: left;
}
.km_equipment_table td:nth-child(2) {
    text-align: center;
}
.km_equipment_table td:nth-child(3) {
    text-align: right;
}
.km_equipment_table td:nth-child(4) {
    text-align: right;
}
.km_equipment_table td:nth-child(5) {
    text-align: right;
}
.km_equipment_table td:nth-child(6) {
    text-align: right;
}
.km_equipment_table td:nth-child(7) {
    text-align: right;
}

In the WordPress Text editor for the page containing the table, I constructed the HTML code for the table to begin as follows – the style and border specifications probably could have been handled in the style sheet, but the printed output was not ideal without these changes:

<table class="km_equipment_table" style="border-collapse: collapse;" border="1" width="100%">

To handle cases where the filenames on the old web server had to be redirected to the correct page on the new web server, adjustments had to be made to the .htaccess file – .htaccess files found in parent directories will apply to child directories also.  I spent a couple of days trying unsuccessfully to make the page redirections work, and then stumbled on a solution, again using the WordPress package on a Synology NAS as a test bed.  The old website had a number of web page addresses that contained ? characters, such as this one:

/page.php?menu_id=10

To send requests for that page to the WordPress permalinks naming convention that uses the article titles as the web page address, I added the following to the .htaccess file (R=301 indicates that this is a permanent redirect, while L indicates that this is the last rule that should be processed):

RewriteCond %{QUERY_STRING} menu_id=10
RewriteRule (.*) /industries/? [R=301,L]

To redirect a web page found on the old server that was named large_machining.htm to the appropriate page in WordPress, I added the following to the .htaccess file:

RewriteRule ^large_machining.htm/?$ /large-machining/ [R=301,L]

Those rewrite rules must be wrapped in the file, so a portion of the file may appear as follows – note that if there were a page on the old server with a menu_id=100, that entry must appear before the entry for menu_id=10, and the entry for menu_id=10 (and menu_id=11) must appear before the entry for menu_id=1 – otherwise the person attempting to visit the website from a saved bookmark (favorite) may be sent to the wrong web page:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
     
    # industries
    RewriteCond %{QUERY_STRING} menu_id=10
    RewriteRule (.*) /industries/? [R=301,L]
     
    # careers
    RewriteCond %{QUERY_STRING} menu_id=12
    RewriteRule (.*) /careers/? [R=301,L]
     
    # ... many more
    RewriteCond %{QUERY_STRING} menu_id=1
    RewriteRule (.*) /about/? [R=301,L]
    
    # Misc. redirects
    RewriteRule ^ABOUT.HTM/?$ /about/ [R=301,L]
    
    RewriteRule ^large_machining.htm/?$ /large-machining/ [R=301,L]
</IfModule>
    
# BEGIN WordPress
<IfModule mod_rewrite.c>
    
    # ... Standard WordPress entries here
</IfModule>
# END WordPress

Part 4: Idle Thoughts (Yes, this was supposed to be a three part article)

So, I was becoming impatient while waiting for the web development company to finally push the new version of the website over to the publically accessible website (I expected this process to happen a week ago, last Friday, but it did not go live until midnight today).  This past Monday I was beginning to think that this task was too much for the web development company.  So, I set out to learn a bit about Amazon’s AWS hosting options.  It turns out that Amazon offers a t2.micro hosting solution that is free for a year.  At the time I was almost completely unfamiliar with Amazon’s hosting offerings, so I signed up for a free account.  After puttering around for a bit, I managed to create an instance on AWS, figured out that I needed to assign an elastic IP address to the instance, determined how to connect to the instance using Putty, installed LAMP in the t2.micro instance, set up an FTP server in the instance, installed WordPress in the instance, imported the new website’s contents, changed the domain’s global DNS settings, and had a fully functional website in probably five or six hours – not too bad for having so little experience with those items. The website in the t2.micro instance crashed three times on me this past Tuesday during final testing due to consuming the full 1GB of memory that is allocated to those types of instances, but at least I think that I was able to demonstrate that the web development company was either seriously dragging this project out longer than necessary, or there is a problem with their method of assigning priority to projects.  The new website went live this morning with a couple of minor issues: 1) No one onsite at my company could view the website because the development company insisted on removing the www. portion of the website address (at companies that use an Active Directory internal domain, that causes the DNS name resolution to point to the web server on one of the internal domain controllers – none of those domain controllers has web server software installed); 2) The .htaccess file that I spent a couple of days creating was overwritten by a file written by the developer – that file either redirected most of the traffic to the wrong page (see my notes above about why the order of entries in that file is important), or sent the person to a 404 error page (page not found) for all historic web page filenames; 3) College was spelled as Collage.  Oh, well.  Close counts in website development, just as it does with horse shoes and hand grenades.





On the Topic of Technology… 7

26 09 2014

September 26, 2014

(Back to the Previous Post in the Series)  (Forward to the Next Post in the Series)

As I was walking from the back of the facility where I work to my office just recently, I started repeatedly saying to myself as I approached the office door, “that huge computer was not here when I started calling my office a second home“.  I guess that I have worked at the same place for so long that I became blind to the interesting technology gadgets (if you can call multi-million dollar computers simply just gadgets) that surround me on a daily basis.

A couple of years ago BlackBerry released a small 8″ tablet, and Motorola released a 10″ Xoom tablet (I think that Motorola sold out their Mobility division to Google, who then sold that division to a farmer to use as fertilizer).  At the time the Xoom and BlackBerry tablets were released, my boss was really excited about the Apple iPads, but he did not care to spend $500 or more of his own money for a toy to use at home.  He had a Windows computer at home, but he seemed to always view that computer as excessively slow (mostly when viewing websites), even though he spent close to $3,000 on the computer six years earlier.  I am not much of an Apple fan, so I decided to have a little fun with my boss’ situation.

On the day that the Xoom tablet became available on Amazon, I placed an order for the tablet.  When it arrived, I brought it into work and showed the boss how quickly it could pull up web pages, along with its support of Adobe Flash playback (the iPad never supported Adobe Flash).  Yet, he continued to go on about the iPad, even showing me newspaper articles written by tech gurus that boasted about the fantastic features of the iPad.  A year earlier I had bought a small Windows netbook with a 10” display, trying to convince him that such a netbook was even better than an iPad, so obviously that prior attempt failed.

When the BlackBerry tablet was released, I made a special trip to Best Buy just to grab the tablet.  I set the tablet up to work with the BlackBerry phone that I had at the time.  Oh neat, I am able to look at the company emails that I receive on the phone using the tablet – certainly, that will convince the boss that something is better than the iPad.  I showed my boss, who was also using a BlackBerry phone at the time, the neat BlackBerry tablet that could not only quickly pull up web pages (along with showing Adobe Flash contents), but could also show company emails and use the phone as a mobile hotspot for viewing web pages.  He spent a couple of minutes looking over the BlackBerry tablet before handing it back to me.  I found a couple more newspaper articles about the iPad on my desk in the weeks that followed.

On a Sunday afternoon, I decided to do some video testing with the two tablets, in a final attempt to convince the boss that something other than an iPad is ideal for his use at home.  I took the two tablets to my second home (that’s the place where my office, and all of those huge computers are located), and decided to do a head to head video test with the two tablets.  I planned to show the best looking video from the two tablets to the boss, and finally win him over.  I held the two tablets side-by-side as I walked down the isles of the huge computers.  As I walked, I wondered what that 40,000 pound part was doing in the big pit that was dug for one of the computers that was expected to arrive in another month or two.  No matter, I continued with my video testing, holding the tablets at head level as I walked.  I received some strange looks from the other employees as I walked about – I simply reassured the other employees that I was just trying to impress the boss.  I took the tablets home and processed the video from the tablets to eliminate meaningless portions of the video.  It seems that both tablets produced 720P video at either 29 or 30 frames per second that was virtually identical in video quality, but the BlackBerry video would playback directly in the Windows Media Player, while the Xoom video required conversion to a compatible format.  I showed the boss the resulting video, that not only could the BlackBerry tablet quickly pull up web pages (along with showing Adobe Flash contents), show company emails and use the phone as a mobile hotspot for viewing web pages, but also record 720P video that easily plays back on your Windows computer at home.  The boss thought for a minute or two, and then said, “did you have a chance to read Walt Mossberg’s latest Wall Street Journal article, there is a new iPad out now.”

Ah, fond memories.

I recently found the video clips that I recorded using the tablets back in 2011, and after reviewing the videos, I still can’t see much difference between the videos captured by either tablet.  The video looks nice when playing back, but pausing either video to take a screen capture results in a blurry single-frame mess 90% of the time.  The video showed the big pit that was dug for the large computer – yep, that pit now contains a multi-million dollar computer, and the wall that had been next to the pit was removed during a later expansion project.

In the nearly five years since I created the first article on this blog, I really have not said much about the company where I work.  I have posted a lot of Oracle Database book reviews on Amazon, as well as several reviews of security cameras.  Some readers on Amazon were convinced that I worked for a couple of book publishing companies, writing fake book reviews to promote the publishers books; people who actually read the book reviews should know better than that – the reviews are brutally honest.  Some other customers on Amazon thought that I was working for a security camera company and/or living in California; no, not the case.  As a result, I put together an article that shows some of the interesting technology and multi-million dollar computers that are located just feet from my office at work.  In the article, I included some still frames from the video that I captured in the walk through with the tablets in 2011.

Below are three pictures from the article that I recently posted.  I am still trying to come up with good captions for the last two pictures, captions such as “taking a break” and “breaking in a new truck” seem to come in mind.

Cincinnati CL-707 Laser Burner Slicing Through 1In the Deep EndNeed a Bigger TRuck





On the Topic of Technology… 6

16 03 2014

March 16, 2014

(Back to the Previous Post in the Series)  (Forward to the Next Post in the Series)

It has been a while since my last post on this blog – I guess that the simple answer is that I was busy with a lot of non-Oracle Database related items, and was suffering from a bit of a writer’s block (nothing that a block dump can’t fix?).  I am expecting to soon receive the annual bill from WordPress for keeping this blog free of advertisements, as well as a bill for allowing the customized blog theme.

So, given the number of months since my last blog post,  I took the time to update the list of the top five most viewed articles for the past quarter.  The number one article shows how to install the Nagios network monitoring software on a Synology NAS (actually three different Synology NAS units), which means that a low cost NAS unit could be used to not only verify that a server used with Oracle Database responds to a ping request, but also that an Oracle database is reachable and healthy enough to provide a resultset for a simple SQL statement.  The number two article shows how to do a little mathematics with the help of Oracle Database, approximating the distance between two longitude and latitude coordinates.  The number three article shows how to use a programming language that was last updated in the late 1990s with the latest Microsoft operating system and what was the latest version of Oracle Database.

The advancement of technology certainly means that it is important for IT professionals to try staying on top of the advancements in their technology niche, without completely cutting ties with technology of the past, as illustrated by the current number three article on this blog.  For me, that means buying and then reading cover to cover various books, reading articles, and experimenting with technology.  It helps that I am an IT manager in addition to being an Oracle DBA, so my technology niche is rather broad.  In December 2013 I placed an order for the updated version of “Troubleshooting Oracle Performance“, in part because I enjoyed the first version of that book so much that I read it twice, and also because I have not had sufficient time to experiment with Oracle Database 12c – it appears that the second edition might ship next month.  Someone recently left a comment on another book that I reviewed here and on Amazon – I tried ordering that book twice without success, and now there is apparently a new version of the book on Amazon that includes coverage of Oracle Database 12c, and the book is in stock!  Someone will have to spend the $56, write a review, and let me know if the author fixed the items that I and readers of this blog so patiently and clearly mentioned in 2010.  Anyone interested in the challenge?

As I mentioned, the scope of my job responsibilities extends far beyond that of Oracle Database.  I just recently migrated the company’s email system from Microsoft Exchange 2007 to Microsoft Exchange 2013 SP1.  Anyone who remembers the fun of typing cryptic code on a command line would enjoy this experience.  Simply moving the public folders from the old server to the new server was an excellent example of command line fun, reminding me of the fun that I had years ago trying to compile X.509 certificate support into a Linux kernel.  One book that I read and reviewed was extensively detailed on the topic of public folders, yet the commands that were found in the book failed to execute without returning an error message at step 1.  The other book that I read and reviewed more or less skimmed the topic of public folders, so it was of no help for the task at hand.  No problem, I will just go to the source, Microsoft, for the solution.  A recent article on Microsoft’s site clearly listed all of the steps required to move the public folders from Exchange Server 2007 to Exchange Server 2013… all except for one very important step.  So, I am running command after command on the servers trying to move the public folders from the one server to the next, only having a partial idea of what these commands are doing.  Everything is going great, until I execute the last command listed here:

Get-PublicFolder -Recurse | Export-CliXML C:\PFMigration\Legacy_PFStructure.xml
Get-PublicFolderStatistics | Export-CliXML C:\PFMigration\Legacy_PFStatistics.xml
Get-PublicFolder -Recurse | Get-PublicFolderClientPermission | Select-Object Identity,User -ExpandProperty AccessRights | Export-CliXML C:\PFMigration\Legacy_PFPerms.xml
Get-PublicFolderDatabase | ForEach {Get-PublicFolderStatistics -Server $_.Server | Where {$_.Name -like "*\*"}}
Set-PublicFolder -Identity <public folder identity> -Name <new public folder name>
Get-OrganizationConfig | Format-List PublicFoldersLockedforMigration, PublicFolderMigrationComplete
Set-OrganizationConfig -PublicFoldersLockedforMigration:$false -PublicFolderMigrationComplete:$false
Get-PublicFolderMigrationRequest | Remove-PublicFolderMigrationRequest -Confirm:$false
Get-Mailbox -PublicFolder 
Get-PublicFolder
Get-Mailbox -PublicFolder | Where{$_.IsRootPublicFolderMailbox -eq $false} | Remove-Mailbox -PublicFolder -Force -Confirm:$false
Get-Mailbox -PublicFolder | Remove-Mailbox -PublicFolder -Force -Confirm:$false
.\Export-PublicFolderStatistics.ps1 <Folder to size map path> <FQDN of source server>
...

Spot the error?  Why is this server telling me that I need to provide a comma separated list of parameters when I execute the Export-PublicFolderStatistics.ps1 script?  So, I submit the script again with commas separating the parameters – no the same error is returned.  Must be a problem where I need to specify the parameters in double quotes also – no the same error is returned.  What the four letter word?  That is right, the return of trying to compile X.509 certificate support into the Linux kernel roughly a decade ago, only now on Microsoft’s premium messaging platform.

So, what is the missing step?  Exchange Server 2007 ships with Microsoft PowerShell 1.0 – this command requires Microsoft PowerShell 2.0 to execute, yet that requirement was never mentioned.  Oh yeah, we forgot a step, get over it – you have another set of 10 cryptic commands to enter – only to be greeted with a failure message during the public folder migration, stating that the migration failed because some folder name that once existed on Microsoft Exchange 5.5 contains a character that is now considered an invalid character in its name.  These problems never happen with an upgrade in the Oracle Database world, do they?  Advancement of technology, or Back to the Command Line.

I have also spent a bit of time experimenting with IP security cameras.  I put one in my vehicle and went for a drive.  Ah, 1969, someone obviously has not finished compiling the time saving feature into the camera’s firmware? (Click the picture for a larger view.)

NC-239WF-LicensePlateTest-Mod

Let’s try a different stop light – these two cars are either turning the wrong direction (obviously an indication of a bug in the camera’s firmware), or are running a red light. (Click the picture for a larger view.)

NC-239WF-RedLightTest

The camera did not pick up much interesting in the vehicle, so I set it up just in time to catch a game of what appears to be football… or maybe it was a game of sock-her? (Click the picture for a larger view.)

NC-239WF-DeerFightTest

Technology is fun, except when it hit you in the nose.





On the Topic of Technology… 5

12 07 2013

July 7, 2013

(Back to the Previous Post in the Series) (Forward to the Next Post in the Series)

As many readers of this blog are probably aware, Oracle Corporation has released Oracle Database 12.1.0.1 so far for the Linux, Solaris, and Windows platforms.  Oracle Database 12.1.0.1 may be downloaded from Oracle’s OTN website.  This article is not about Oracle Database 12.1.0.1, at least not specifically about that version.

In the previous article in this blog series last year, I mentioned experimenting with a Synology DiskStation DS212+, as well as a couple of IP based 640×480 resolution security cameras.  Since that time I have had the opportunity to purchase a couple of additional NAS devices including a Synology Diskstation DS112J, Synology Diskstation DS412+, and Synology Diskstation DS1813+.  The DS212+ and DS112J NAS devices have ARM type processors, while the DS412+ (32 bit?) and DS1813+ (64 bit) have Intel Atom D2700 series processors (the processor series for other Synology processors may be determined by visiting this link).  The processor type in the NAS partially determines the native capabilities of the NAS, as well as what else may be done with the NAS.  Setting up the Synology NAS devices to support FTP server functionality is fairly easy to accomplish, regardless of the processor type.  That FTP server functionality helps to support the upload functionality of the IP based security cameras.

As an experiment shortly after buying the Synology DiskStation DS212+, I attempted to install the network monitoring tool Nagios, in part to allow keeping track of which IP cameras were offline.  I hit a bit of a brick wall trying to find a precompiled package to permit the Nagios server functionality to run on the Synology DiskStations, which at the core run a version of Linux.  The closest thing that I could find was a plugin for Nagios to permit Nagios running on another machine to monitor a Synology NAS.  I first worked with Red Hat Linux in 1999, implemented dual inline manually-coded iptables firewalls based on a stripped down Red Hat Linux in early 2002, compiled/built a Linux based X.509 certificate supporting VPN server before the Linux kernel supported X.509 certificates (I tried compiling a patched version of the Red Hat kernel, patched with X.509 support, but eventually gave up and compiled the Working Overloaded Kernel), and even tried running Red Hat Enterprise Linux with Samba and Windbind as a member of the company’s file server team.  I first worked with Nagios in 2002, when one of my brothers introduced me to the Linux based network monitoring tool (previously called NetSaint).  Needless to say, I have experience working with Linux and manually compiling software on that platform, but that experience is apparently quite rusty.  The attempt to compile the Nagios source code on the Synology DiskStation DS212+ came to an abrupt halt when I received a message during the compile process essentially stating that the ARM type CPU (Marvell Kirkwood mv6282) did not support fine timer resolutions.

A couple of months later, I tried compiling the Nagios source on the Synology DiskStation DS412+, which features an Intel CPU architecture.  I encountered a couple of unexpected snags in the compile process, and had to put the project on hold for several months.  The paths to the various files on the Linux operating system running on the DiskStation differs a bit from the paths used by the Red Hat variants of Linux – that lack of standardization across the various Linux distributions has frustrated me from time to time over the years.

I recently purchased and reviewed a Synology DiskStation DS1813+.  In the review, I stated the following before testing the theory:

“Additionally, ipkg support permits the installation of roughly 900 additional applications, including C++ compilers – which in theory suggests that the source for the Nagios network monitoring utility can be downloaded and compiled on the DS1813+.”

I am curious to know whether or not anyone is able to get the Nagios server software to run on a Synology DiskStation DS412+ or DS1813+.

I suppose that I should not have proposed that the Nagios network monitoring utility might work on the DiskStation without actually confirming that the utility will work.  I am now able to confirm that the Nagios network monitoring utility will execute on the Synology DiskStation DS1813+, although the check_http plugin failed to compile.  The installation is anything but straight-forward – no how-tos that are close to being useful, and no Setup.exe to double-click.  The following screen capture also does not help (non-root users are not permitted to use the ping command on the DiskStations):

TopicOfTechnology5-1

At this time, I cannot provide a detailed instruction list for running the Nagios network monitoring utility on a Synology DiskStation.  However, as a starting point it is necessary to add ipkg support to the DiskStation.  The following ipkg items might be necessary: optware-devel, gcc, libtool, mysql, apache, openssl, openssl-dev, sendmail, inetutils.  With a bit of experimentation (and luck), you might see something similar to this when typing the ps command in a telnet session (I actually typed the command a second time so that the column headings would be visible – there certainly are a lot of background processes on the DiskStation):

TopicOfTechnology5-2

As I found out, just because Nagios is in the process list, that does not mean that it is able to do much of anything useful.  A work-around for the non-root ping issue is needed (I might have hinted part of the solution when listing the various ipkgs), as well as a work-around for the non-root sendmail problem that I did not mention.

When Nagios is working properly, unplugging a monitored device should result in an email message being sent (of course, if you unplug your computer, you probably will not receive an email message stating that the computer is down 🙂 ):

TopicOfTechnology5-3

There appear to be several Nagios plugins to monitor Oracle databases, although I have not had a chance yet to determine if any of those plugins will compile and work on a Synology DiskStation.  In theory it should wor… wait, I am not headed down that path yet!

In addition to a Synology DiskStation DS212+, the previous article in this series also showed a couple of smart 640×480 resolution IP cameras.  At the time of the previous article, I did not fully comprehend the usefulness of smart IP cameras.  Roughly 30 IP based cameras later, I now have a better understanding of their usefulness and limitations.  Last year I wrote reviews for three 640×480 model cameras here (it appears that Amazon now has this review attached to a different camera), here (it appears that Amazon now has this review attached to a different camera), and here (OK, there is a forth camera included in this review due to a model change over).  I was also burned badly (at a loss of $1343) when I bought two 1080P cameras last year that could not meet (or even approach) the manufacturer’s claims for the product.  All of those reviews include video samples produced by the cameras.

This year I bought and reviewed a couple of smart 720P resolution IP cameras, as well as a couple of different (from last year’s 1080P) smart 1080P resolution IP cameras.  As before, the reviews include sample video clips recorded by the cameras (the 720P and 1080P video was uploaded at the native resolution, but it appears that Amazon uses a pretty aggressive compression algorithm, which leads to some lost video quality).  The new 720P and 1080P cameras are not perfect, but the manufacturer appears to be taking steps to address the weaknesses that I outlined in the reviews.  I was sent another updated firmware for the 1080P cameras, as well as an updated PDF that includes the instructions that were missing from the included printed manual.  The support person for the camera company also stated that their website is currently under development, and will probably be online in the next 30 days.  My review mentioned the lack of success at using the recommended P2PCam264 app on a Motorola Xoom tablet for viewing the live video feed from the smart 720P and 1080P cameras.  The support person suggested using the AnyScene app on the Motorola Xoom tablet for viewing the live feed – that app seems to work.  The AnyScene app, while seemingly lacking the sound feed from the cameras, might even work a little too well.  I brought the Xoom tablet to a different network, only to find that the app is somehow able to still pull a live video feed from any of the configured cameras on the other network without poking any holes in the firewall on either network, and universal plug and play (uPNP) is disabled (below is a low resolution cell phone captured picture).  I am now left wondering what level of security risk this plug and play technology might pose.

TopicOfTechnology5-4

Sample PNG Generated from 720P Camera’s Video (Click to Display Larger Version):

topicoftechnology5-8

Sample PNG Generated from 1080P Camera’s Video (Same Scene as the Above Example – Click to Display Larger Version):

topicoftechnology5-9

Sample JPG 720P Image from an Edited Video (the 1080P video suffers from fewer out of focus problems and is the same resolution – just with a roughly 50% wider and taller viewing area):

TopicOfTechnology5-6





Unexpected Timer Resolution, Unexpected Parked CPUs, Unexpected Power Consumption

19 04 2013

April 19, 2013 (Modified May 11, 2013, June 5, 2014)

This blog article is not purely Oracle Database specific, yet it may have some relevance to companies that run Oracle Database on the Windows Server platform (for those DBAs lucky/unlucky enough to run Oracle Database on the Windows Server platform, you may find this article interesting).

I am in the process of setting up a couple of new Windows servers to perform various non-Oracle Database tasks.  I noticed that one of the servers had an odd issue – the server would occasionally become very slow at responding to mouse movements and keyboard input, for instance taking 30 seconds to move the mouse pointer a short distance across the screen.  These servers are running Windows Server 2012, which shares the same kernel and includes much the same features as Windows 8 – with the exception that the server operating system opens to the desktop rather than Windows 8’s new start screen.

Two years ago I wrote a brain teaser article that asked how it was possible that a 10046 extended SQL trace could output c=15600,e=510 on a line of the trace file when executing a SQL statement without using parallel query – essentially asking how it was possible to consume 0.015600 seconds of CPU time in 0.000510 seconds of elapsed time when the SQL statement was restricted to running on no more than one CPU.  In the comments section of the article I mentioned the ClockRes utility, but did not provide a link for the download of the program.  So, I thought that I would run the ClockRes utility on one of the new servers, make a change to the server, and then run the ClockRes utility again:

UnexpectedClockResOutput

As can be seen above, on the first execution of ClockRes the Current timer interval was 1.001 ms, while on the second execution of the ClockRes program the Current timer interval was 15.626 ms.  There is an odd similarity between that 15.626ms time (which oddly exceeds the reported Maximum timer interval of 15.625ms) and the c=15600 reported in the Oracle 10046 extended SQL trace file.  So, what change did I make to the server between the first execution of ClockRes utility and the second execution?  For now I will just say that I stopped one of the background services on the server (more later).

I recall performing an experiment a couple of years ago with Oracle Database.  I downloaded a utility that offered to change the Windows default timer resolution from 15.625ms to 1.0ms.  That utility did in fact change the Windows timer resolution, resulting in Oracle Database outputting c= values in increments of 1000, rather than in increments of 15600.  If I am remembering correctly, a second outcome of the experiment was a decrease in performance of the test Oracle database on the computer due to the higher resolution of the Windows timer.

Could the change in the resolution of the Windows timer from the Windows default of 15.625ms to 1.001ms be responsible for the occasionally sluggish performance of the server?  One article that I found (and unfortunately did not save the link to) claimed that adjusting the Windows timer from the default of 15.625ms to a lower value, 1ms for example, could cause a significant negative impact in multitasking system performance (roughly 30% decrease, if I recall correctly).  I located an article on Microsoft’s website that offered some level of clarification, below is a short quote from the article:

“Applications can call timeBeginPeriod to increase the timer resolution. The maximum resolution of 1 ms is used to support graphical animations, audio playback, or video playback. This not only increases the timer resolution for the application to 1 ms, but also affects the global system timer resolution, because Windows uses at least the highest resolution (that is, the lowest interval) that any application requests. Therefore, if only one application requests a timer resolution of 1 ms, the system timer sets the interval (also called the “system timer tick”) to at least 1 ms. For more information, see “timeBeginPeriod Function” on the MSDN® website.

Modern processors and chipsets, particularly in portable platforms, use the idle time between system timer intervals to reduce system power consumption. Various processor and chipset components are placed into low-power idle states between timer intervals. However, these low-power idle states are often ineffective at lowering system power consumption when the system timer interval is less than the default.

If the system timer interval is decreased to less than the default, including when an application calls timeBeginPeriod with a resolution of 1 ms, the low-power idle states are ineffective at reducing system power consumption and system battery life suffers.”

The above mentioned Microsoft article also suggested running the following command from the Windows command line:

powercfg /energy

I had actually executed the above command before running the ClockRes program for the first time, and again after running the ClockRes program for the second time.  A very small portion of the powercfg generated HTML file follows, generated prior to the first execution of ClockRes:

Platform Timer Resolution:Platform Timer Resolution
The default platform timer resolution is 15.6ms (15625000ns) and should be used whenever the system is idle. If the timer resolution is increased, processor power management technologies may not be effective. The timer resolution may be increased due to multimedia playback or graphical animations.
Current Timer Resolution (100ns units) 10009
Maximum Timer Period (100ns units) 156250

Platform Timer Resolution:Outstanding Timer Request
A program or service has requested a timer resolution smaller than the platform maximum timer resolution.
Requested Period 10000
Requesting Process ID 536
Requesting Process Path \Device\HarddiskVolume4\PROGRA~2\APC\POWERC~1\agent\pbeagent.exe

This is the same section of the generated HTML file, generated after the second execution of ClockRes:

Platform Timer Resolution:Platform Timer Resolution
The default platform timer resolution is 15.6ms (15625000ns) and should be used whenever the system is idle. If the timer resolution is increased, processor power management technologies may not be effective. The timer resolution may be increased due to multimedia playback or graphical animations.
Current Timer Resolution (100ns units) 156261

That is potentially interesting.  The output of powercfg stated that PROGRA~2\APC\POWERC~1\agent\pbeagent.exe requested a timer of 1.000 ms, which then changed the Windows server system-wide timer to 1.0009ms.  Interesting?  PROGRA~2\APC\POWERC~1\agent\pbeagent.exe resolves to the “APC PBE Agent” service in Windows, which is a component of the American Power Conversion (APC) PowerChute Business Edition software.  That software interfaces with an attached UPS to provide a gentle shutdown of the server in the event of an extended power outage.  The “APC PBE Agent” service happens to be the service that I shut down between the first and second execution of the ClockRes utility.

Interesting?  Does that suggest that installing the APC PowerChute Business Edition software on a server potentially has a significant impact on the performance of that server due to the program’s insistance on changing the Windows system-wide timer resolution to 1ms?  A quick observation indicates that the change made by the APC software to the Windows system-wide timer resolution does NOT apparently affect the reporting of the c=15600 entries in an Oracle Database 10046 extended SQL trace when the APC software is installed on the server.  The question remains whether or not this APC software could significantly decrease the performance of that Oracle Database software (potentially by 30%, as suggested in the one unnamed article).

——

The Windows Server that is experiencing occasionally jittery mouse and keyboard input is reasonally high-end for a Windows server: Intel Xeon E5-2690 8 core CPU at 2.9GHz (with hyperthreading enabled, giving the appearance of 16 CPUs in Windows), 64GB of memory, RAID controller with 1GB of battery backed cache, 16 internal 10,000 RPM hard drives, two gigabit network adapters in a teamed configuration, etc.  It should require a substantial load on the server to cause the jittery mouse and keyboard input behavior.

The power option plan in Windows was set to High Performance, while the default plan in Windows Server is Balanced.  Various articles on Microsoft’s website state that the Balanced plan allows the server/operating system to use CPU speed throttling (reducing the CPU speed from the stated speed rating, 2.9GHz in the case of this server), and core parking (essentially putting one or more CPU cores to sleep) in order to reduce energy consumption.  Some articles on Microsoft’s site indicate that, at least with Windows Server 2008, that CPU parking may increase IO latencies – that, of course, would be bad if Oracle Database were installed on the server.  Other articles on Microsoft’s site indicate that there are bugs, at least with Windows Server 2008, related to core parking which causes the parked cores not to wake up when the CPU load increases.  I wonder if this particular bug is playing a part in the performance issue faced in this very recent Usenet thread that describes poor performance of Oracle Database running in Hyper-V on Windows?

Here is a screen capture of the Power Options window and Task Manager on the Windows Server 2012 machine that is experiencing occasionally jittery mouse and keyboard input (screen capture taken when the server was mostly idle):

UnexpectedPowerOptionsTaskManager

Notice the inconsistency?  The server’s CPU is throttled down from 2.9GHz to just 1.16GHz while the power option plan is set to High Performance.  The Microsoft published “Performance Tuning Guidelines for Windows Server 2012” document on pages 16-17 states the following (I highlighted some of the words in red):

Balanced (recommended): Default setting. Targets good energy efficiency with minimal performance impact.  Matches capacity to demand. Energy-saving features balance power and performance.

High Performance: Increases performance at the cost of high energy consumption. Power and thermal limitations, operating expenses, and reliability considerations apply.  Processors are always locked at the highest performance state (including “turbo” frequencies). All cores are unparked.

Power Saver: Limits performance to save energy and reduce operating cost.  Caps processor frequency at a percentage of maximum (if supported), and enables other energy-saving features.”

Well, that is interesting, and is inconsistent with the above screen capture.  Incidentally, when the server was experiencing the worst of the occasionally jittery mouse and keyboard input, the CPU utilization was hovering around 6% and the CPU speed was still coasting at 1.16GHz to 1.18GHz, the network performance hovered between 600Mbps and 1100Mbps, and the server’s internal hard drives barely noticed the traffic passing to/from the disks through the network interface (lower than 75MB/s and 137MB/s, respectively).  6% CPU utilization causes the mouse and keyboard input to become jittery?  With hyperthreading enabled, there is essentially 16 available CPU seconds per each second of elapsed time.  A quick check: 1/16 = 0.0625, so 1 (hyperthreaded) CPU at 100% utilization would be reported as a system-wide utilization of 6.25%.  Interesting, but is that statistic relevant?

I happened to have the Windows Resource Monitor open during one of the jittery episodes.  The Resource Monitor showed, shockingly, that 14 (possibly 15) of the hyperthreaded “CPUs” were parked!  That result is also in conflict with the Microsoft document mentioned above regarding “all cores are unparked” when the High Performance power plan is selected.  So, at 6% CPU utilization the server was CPU constrained.  Modifying the setting in the server’s BIOS that controls whether or not cores may be parked, so that the cores could not be parked, fixed the issue in Windows Server 2012 that resulted in the 30 second delay that accompanied moving the mouse pointer a short distance across the screen.

The server still exhibits a bit of jittery behavior with mouse and keyboard input when the server’s teamed network cards are heavily used for file transfers to the server, but at least the CPU activity is no longer confined to a single hyperthreaded “CPU”:

UnexpectedResourceMonitor

Considering that this server was ordered from the manufacturer as “performance optimized”, I am a bit surprised at the power consumption of the server.  The server was ordered with dual (redundant) 1100 watt power supplies.  With the CPU’s 135 watt maximum TDP (per Intel: “Thermal Design Power (TDP) represents the near maximum power a product can draw for a thermally significant period while running commercially available software.”), 16 hard drives, and 64GB of memory, I fully expected the server to consume between 700 and 900 watts of electrical power.

Here is the server’s power consumption when the server is lightly loaded with roughly 68 running processes (note that the server is connected to a 120 volt power outlet):

UnexpectedPowerConsumptionLittleLoad

Here is the server’s power consumption when the server is moderately loaded with between 600Mbps and 1100Mbps of network traffic (the mouse pointer was slightly jittery at this point):

UnexpectedPowerConsumptionNetworkLoad

So, the server consumes 1.2 amps (126 watts) when lightly loaded and 1.4 amps (154 watts) when moderately loaded.  Keeping in mind that many of the popular incandescent light bulbs require 100 watts of power (note that some governments have now restricted the manufacturing of high wattage incandescent light bulbs), this server is consuming just a little more electrical power than a light bulb that might have been hung overhead just a decade or two ago.

One of the common arguments for server virtualization is energy savings – the above screen captures may suggest that energy savings may not be a significant cost-savings factor for virtualization with modern server hardware.  One might question how much energy is really being saved when the network interface is maxed out by a single virtualized server, just 6% CPU utilization results in a jittering mouse pointer, and there are eight to ten virtualized servers stacked on the physical hardware (all competing for the scarce CPU and network resources).

Added May 11, 2013:

Dell BIOS setting to enable or disable CPU parking in Windows Server 2012:

UnexpectedPowerConsumptionBIOSProcIdle

With the BIOS option set to enabled, disk activity caused by network traffic results in occasionally jittery mouse movements on the server.  Based on a bit of research, installing the Hyper-V role on either Windows Server 2012 or Windows 8 may disable CPU throttling and/or disable CPU parking.

Added June 5, 2014:

I finally had sufficient time to fully analyze this problem, where a 2.9GHz CPU in a Dell PowerEdge T620 server crawled along at a leasurely pace of about 1.16GHz, actually throttling back performance further as demand for the server’s resources increased.  A second Dell PowerEdge T620 server with a 2.6GHz CPU that was purchased at the same time also coasted along at roughly 1.16GHz, but that server did not seem to throttle back performance further as demand for the server’s resources increased.

As a review, the screen capture shown below at the left shows the Windows Server 2012 Power Options settings and the Performance tab of the Task Manager.  The screen capture below at the right shows the Windows Server 2012 Power Options settings and the Performance tab of the Task Manager after fixing this particular problem – note that the 2.9GHz CPU is now essentially overclocked at 3.28GHz (it has operated at roughly that speed since the fix).

UnexpectedT620PowerOptionsTaskManager UnexpectedT620PowerOptionsTaskManager2

The 2.9GHz PowerEdge T620 and the 2.6GHz PowerEdge T620 are both Active Directory domain controllers and internal DNS servers (along with supporting other tasks), so the occasionally slow (or extremely slow) performance of the servers negatively impacted the performance of other servers as well as client workstations.

There was a BIOS firmware update released in the third quarter of 2013, which was supposed to address some CPU throttling issues – that BIOS update did not seem to help the problem that I experienced.

I thought that the low power consumption of the big server with the 2.9 GHz E5-2690 8 core CPU was a significant clue when I tried troubleshooting the server a year ago since that CPU is rated to consume up to 135 watts and the server liked to hover between 120 watts and 140 watts regardless of the server’s workload.  The Dell T620 (and other recent Dell servers) has some pretty sophisticated power management capabilities.  Dell sells a utility that is able to alter the electrical power profile of a server, and I thought that Dell might have imposed a 140 watt limit on the server for some reason, but I could not find where that limit was specified.  The 2.9 GHz E5-2690 8 core CPU apparently has some additional electrical power limiting capabilities.  A year ago I even tried downloading a demo of Dell’s power management utility – that did not help resolve the issue (I think that the installation might have caused some other issues that I had to fix).  Last week Tuesday I read the following articles:
http://www.dell.com/learn/us/en/19/financial-services-markets-solutions-processor-acceleration-technology
http://www.dell.com/us/business/p/dell-openmanage-power-center/pd
http://www.intel.com/content/www/us/en/data-center/data-center-management/how-to-configure-node-manager-video.html
ftp://ftp.dell.com/Manuals/Common/poweredge-r720_Concept%20Guide_en-us.pdf
http://en.community.dell.com/techcenter/power-cooling/w/wiki/3536.openmanage-power-center-faq.aspx

I rebooted the server, pressed F2, and dug around in the settings a bit.  I found that the System Profile Setting was set to “Performance per Watt” (I believe that this was how it was set when it left the Dell factory).  I changed that setting to “Performance”, saved the changes, and rebooted the server again.  The server is now consuming 200+ watts, and the CPU is freely exceeding its rated speed.  Once in the System BIOS settings, the pictures below show the configuration changes to remove the electric power cap, thus allowing the server to behave as it should have from the factory:

UnexpectedT620SystemProfile1 UnexpectedT620SystemProfile2

UnexpectedT620SystemProfile3 UnexpectedT620SystemProfile4

I suppose that if a Dell PowerEdge T620 (or similar recent model Dell server) seems to be running a bit slower than expected (note that the particular problem mentioned above is NOT Windows specific – a Dell PowerEdge T620 running Linux should be affected in the same way), you might take a quick peek at the System Profile Setting in the System BIOS to make certain that the System Profile is set to Performance.  As shipped from the factory, two Dell PowerEdge T620 servers purchased this year were NOT affected by the problems mentioned in this blog article.





On the Topic of Technology… 4

7 05 2012

May 7, 2012

(Back to the Previous Post in the Series) (Forward to the Next Post in the Series)

Today’s blog article has an unusual tie in with Oracle.

The last couple of weeks I have been experimenting with video technology.  Computer related video capabilities have certainly changed over the years.  In 1996 I purchased a Sony Handicam and a Video Snappy.  The Sony video camera was capable of recording video with about 480 lines of resolution (NTSC standard) with a 10x optical zoom and recording capability in just 0.6 LUX of lighting.  The Video Snappy plugs into a computer’s parallel (old style printer) port, connects to the Sony video camera by an RCA style video cable, and converts the live video feed from the video camera to still photos.  Combined with a 120MHz Pentium processor, it seemed to be a state-of-the-art setup at the time (of course ignoring the capabilities of the Commodore Amiga/Newtek Video Toaster).  A picture of the Sony/Snappy configuration is shown below.

Of course the video capabilies of current tablets, cell phones, and digital cameras far exceed what was available in the 1990s – video recording with those devices was described in the previous blog article in this series (see the link at the top of this article).

A product that I recently found is the Synology DiskStation DS212+, which has some remarkable features considering that its primary objective is to provide an external hard drive array with RAID 1.  This particular unit ships without hard drives, so I purchased two Western Digital 2TB Green hard drives.  The external hard drive enclosure includes an SD media card reader, a single USB 2 port, two USB 3 ports, and an ESATA port to allow connecting additional external hard drives, printers, and wireless cards.  While not much larger than the hard drives installed in the unit, it certainly offers much more than access to those drives.  The DS212+ offers FTP services (including secure FTP), an iSCSI interface, DHCP services, media sharing services, WordPress, MySQL, PHP, a remarkable operating system that fully renders console screens in a web browser without the use of Adobe Flash (uses HTML 5 and CSS 3), and more.

The Synology DiskStation DS212 line’s disk throughput is limited by a combination of CPU performance and gigabit network maximum transfer speed, with the DS212+ offering the fastest rated transfer speed of roughly 108 MB/s read (very close to the maximum speed of gigabit Ethernet) and 66 MB/s write.  The Synology DS212+ is pictured below, measuring roughly the same height as five books on the topic of Oracle Database.

So, what does the first picture have in common with the second?  More about that commonality later.

Below is a screen capture of the Synology DS212+ operating system GUI (graphical user interface) rendered in Internet Explorer 9, roughly 21 minutes after powering on the unit for the first time and installing the latest version of the operating system.  As seen below, the 2TB drives were 84% formatted roughly six and a half minutes after I connected for the first time (a verify process lasting several hours started immediately after the format, but the hard drives were accessible during this verify process).

The operating system renders resizable, movable, drag and drop capable, right-clickable windows within the web page.  Several of the free optional packages for the DS212+; a resource meter showing CPU, memory, and network utilization; current network connections; and recent log entries are shown in the picture below.

So, what function does the DS212+ serve other than consuming electricity?  That is still a question that I am trying to answer, but I have only had access to the system for a couple of days.  I installed several of the free optional packages (after downloading the latest version from the company’s website), and experimented a bit.  The screen capture below shows the DS212+ playing an Internet radio stream (the channel was essentially selected at random), while simultaneously playing back a 640 pixel by 480 pixel video.

Incidentally, the above video was captured in a completely dark room using infrared lights that are built into the video camera.

As I mentioned at the beginning of this article, over the last couple of weeks I have spent a bit of time working with video technology.  Pictured below are two TriVision NC-107WF video cameras and a SanDisk 32GB micro SD memory card that works with the cameras.  I have also worked with a couple of TriVision NC-107W video cameras, which lack an infrared cut filter, resulting in poor color rendering.

So, what has 16 years of technology progress provided, comparing to the Sony Handycam shown at the start of this article?  The camera shown below records video at 640 pixels by 480 pixels, much like the Sony Handycam, so that feature has not improved much.  The TriVision camera digitally records nearly a month’s worth of video to a memory card that is about the size of a thumbnail, while the Sony Hanycam digitally records between a half hour and two hours of video to a tape that is about the size of an average person’s fist.  The TriVision camera records black and white video in complete darkness due to its built in infrared lights, while the Sony Handycam records excellent completely black videos in the same lighting conditions.

Surprisingly, there are no reviews of the TriVision line of cameras on Amazon.  The cameras appear to be a clone of the (Amazon) highly rated Sharx Security brand of security cameras.  Unlike some of the other security cameras on the market, this camera ships with a well written user manual (with only a small number of typos).  Offering motion detection, support of up to 32 GB of storage, automatic upload of video and still photos to an FTP server, live streaming through desktop web browsers and mobile devices, and a handful of other capabilities, it is hard to believe just how much technology is stuffed into such a small package.  The wireless range when paired with a Cisco 1250 series access point is impressive, but not terribly impressive when paired with a consumer grade Linksys/Cisco wireless router with integrated antennas.  Poor wireless performance is not necessarily a problem, since the camera stores recorded video to the memory card until the specified FTP server is accessible.  The cameras ship with Multi-live software that permits simultaneous viewing and recording of up to 36 cameras directly from the video streams, which is helpful if an FTP server is not configured.

Reliability of the TriVision NC-107WF/NC-107W cameras is still an unknown.  I have experienced occasional glitches accessing the built-in web server, making it impossible to adjust the camera settings (power cycling the camera seems to correct this issue), however those glitches apparently do not affect video recording or uploading of the captured video to FTP servers.

I have also spent a bit of time working with TriVision’s NC-306W outdoor wireless video cameras, which are shown in the picture below.  The NC-306W camera appears to be a clone of the (Amazon) highly rated Sharx video camera.  The web-based configuration of the NC-306W is nearly identical to that of the NC-107WF.  A 32GB memory card with automatic FTP uploading is supported, as is two-way audio (the NC-107WF supports one-way audio).

Since there are no reviews of the Trivision NC-306W, it is difficult to determine the long-term reliability of this camera.  During installation, one of the mounting nuts snapped due to over-torquing, but that nut is only needed for overhead mounting as seen in the picture below (the mounting nut is attached directly between the sun shield at the top of the camera and the white colored dial at the end of the mounting rod).  As with the TriVision NC-107WF/NC-107W cameras, the built-in web server has occasionally stopped responding, but that problem has not affected video capture or FTP upload.

Below is a screen capture of a video stream from a TriVision NC-107WF camera.  The original video quality was slightly better than pictured below (conversion of the screen capture to JPG format caused some detail loss).  The same scene captured by a TriVision NC-107W camera would have a pink, purple, or red cast due to the presence of infrared light (the NC-107WF and NC-306W are able to selectively filter out the infrared light).

I had hoped to upload a couple of videos captured by the cameras, however, WordPress apparently does not support directly uploaded video formats.  I plan to update this blog article as I better understand all of the features that the Synology Diskstation DS212+ offers, and to provide reliability updates of the DS212+ and the TriVision cameras.





Internal Server Error – Contact Your System Administrator

6 12 2011

December 6, 2011

The Oracle OTN forums changed a bit a year or two ago, and in the process I stopped receiving email notifications when new entries were added to discussion threads.  The absence of email notifications was not a significant loss, although at times it is a bit interesting to see how a post in some of the threads changed a week or two after the initial post.  It appears that the OTN staff have corrected the email notification problems, and the speed of the forums seems to have improved significantly since the dramatic performance drop that was caused by an upgrade to the OTN forums a couple of years ago.

An interesting new problem has arrived.  The unbreakable (but free) forums tell me to contact the system administrator after I attempt to log in – the web server claims that the problem is caused by either an internal error or a misconfiguration.  I tried calling the system administrator here, but his phone is busy every time I try to place the call, and remarkably I always get interrupted when I try calling from a different phone.  😉  This is the error that I see immediately after logging in:

What is really irritating is that I received three emails today from OTN telling me that the OP has updated an OTN thread that I responded to, but sadly I cannot reach that thread.  After logging into OTN, I can’t even tell the browser to display forums.oracle.com – this is what I see:

I can make it into Metalink (My Oracle Support) without an issue – I didn’t even need to log in (no password requested):

So, what happens if I click Sign Out in My Oracle Support?  Let’s try and then head back to forums.oracle.com (this seems to work sometimes):

So, the forums work, just as long as you do not care to contribute.  🙂  If we skip the login step, there are a couple of threads in the Community Feedback and Discussion forum about the problems (thread1, thread2).

Let’s log in again… (something comes to mind about the definition of insanity and doing something over and over again):

Out of curiosity, let’s see where forums.oracle.com is pointing:The traceroute is successful (ignore the long ping times – that was caused by other traffic on the Internet connection).

I noted that Google’s 8.8.8.8 DNS server is currently resolving forums.oracle.com also to e4606.b.akamaiedge.net which Google’s DNS server indicates is at IP address 184.25.198.174 (using Google’s DNS server does not change the error message that is displayed in the browser):

Without using Google’s DNS server, forums.oracle.com resolves to 23.1.18.174, as indicated by the trace route output.  I was curious what Wireshark might show when attempting to display forums.oracle.com (while logged in), so I fired it up and then told the browser to refresh the screen twice:

TCP ACKed lost segments…  Would the same problem happen when using Google’s DNS server, which points at IP address 184.25.198.174?  Let’s check:

Not exactly the same, but similar.  I have not spent a lot of time trying to dig through the Wireshark captures, but it is a bit odd that there are still TCP retransmissions (I might need to take a closer look at the Internet connection).

I guess that maybe this blog article drifted a bit.  Anyone else fail to connect 100% of the time, or never have a problem connecting?  I can only imagine the staff changes that probably would take place if one of our internal systems offered less than 95% uptime, much less the 99.99999% uptime that seems to be our internal expectation.  It is important to keep in mind that the OTN forums (even the copyrighted error message in the second screen capture) is a free service offered by Oracle Corporation – the problems will be resolved.





On the Topic of Technology… 3

24 05 2011

May 24, 2011 (Updated May 28, 2011)

(Back to the Previous Post in the Series) (Forward to the Next Post in the Series)

In the previous article in this series I described some of the features of the Motorola Xoom tablet, and some of the reasons why I purchased that tablet rather than the Apple iPad 2.  When I wrote the previous article I could not identify a lot of productive uses for the Xoom – most of the uses seemed to be specific to media consumption (something that is quite expensive when you must pay per megabyte of usage on a wireless plan).

After a bit of thought, I added a BlueTooth keyboard to the Xoom and fired up Oracle Database 11.2.0.2:

The method by which the above works is actually quite simple.  More on that later.  The Honeycomb 3.1 operating system upgrade that was pushed out on May 14, 2011 seems to have corrected the lagging keypresses (and missing keypresses) problem with the on-screen keyboard.

It is quite easy to fill an entire Xoom screen with applications, some of which are free, and others that cost just a couple of dollars.  Fortunately, there are four more screens waiting to be filled with more applications and widgets:

A quick search of the Android MarketPlace for the word Oracle currently finds 94 applications and 246 books, the number of which is slightly surprising, yet might be pointing at a positive that the Xoom can be useful for productive tasks too:

To test the built-in book reader application, I decided I needed to add an Oracle Database related book.  The results are impressive, with line drawings, color graphics, and text just like the original book.  Slick special effects are also a part of the book viewer, most noticeable when changing from one page to another.  The book viewer did crash when viewing one set of pages in this book, however I am not sure if that is still a problem after the rollout of the Honeycomb 3.1 operating system:

Google’s speech to text engine in the Xoom is very impressive, even if it did drop the word “the” in the phrase that I recited (this happened multiple times).  There is a free Google Translate application for the Xoom that accepts speech, converts the speech to text, and then translates the text to another language.  In theory it will also recite the translated text in the foreign language, but that did not seem to work properly when tested:

After the Honeycomb 3.1 upgrade, the Xoom now supports movie rental though the Marketplace application, the tasklist (third button from the left at the bottom of the screen) shows more than the last 5 or so recently started applications, and a couple of other enhancements made their way into the operating system.  Google Goggles and Google Voice Actions are interesting applications for the Xoom, and just might lead to far too many people mindlessly talking to their digital gadgets.  The PocketCloud application was used to VNC into a laptop computer for the first of the pictures in this article – without the external keyboard it was nearly impossible to type into the SQL*Plus window on the Xoom, because the on-screen keyboard did not appear automatically (the on-screen keyboard can be manually displayed with the PocketCloud application).

Prior to the release of the Honeycomb 3.1 operating system for the Xoom I was becoming upset with the number of crashes (force close in Android terminology) and the on-screen keyboard issues, and I was also growing a bit bored trying to find useful applications for the Xoom that did not pose a risk of exceeding my monthly Internet data cap.  So, I wandered into an electronics store.  Nothing interesting here (I later learned that the manufacturer would recall 1,000 of these units from this particular chain electonics store), so I drove to another store located about a block away.  I wandered out with one of these:

The above is a picture of a BlackBerry PlayBook.  The unit has 16GB of memory (larger memory capacities were available), a smaller screen than the Xoom, a good quality video and still picture camera, and an impressive task-switcher that shows off the multitasking capabilities of the unit (shown above).  The PlayBook also shipped with a built-in word processor and spreadsheet application, while I had to pay extra for those features on the Xoom.  The on-screen keyboard is fantastic compared to that found on the Xoom.

In this article I did not include a picture of the search, but searching for the term Oracle in the PlayBook’s App World returned a single result… and that result had nothing to do with Oracle Databases.

So, why did I buy a BlackBerry Playbook?  There is not even a stand-alone email application for the PlayBook, unlike the built-in Gmail application on the Xoom.  Well, if you have a BlackBerry phone that is connected to a BlackBerry Enterprise Server, you can look at and respond to your corporate email:

The above uses the BlackBerry Bridge application on the PlayBook to talk to the BlackBerry phone.  The catch here is that if the group that is responsible for administering the IT policy for the BlackBerry phones has disabled installation of third party applications in the IT Policy for the phone, it is not possible to install the BlackBerry application on the phone that works with the BlackBerry Bridge application on the PlayBook.

The BlackBerry Bridge also permits browsing websites using the phone’s data plan, without incurring an additional monthly cost.  The catch is that content rich websites simply do not display (the limit seems to be around 100KB).  The same websites will display correctly when using the PlayBook’s regular web browser, even if websites insist on sending the mobile version of their web pages.  The same data limit also seems to be present when viewing large email attachments (those that are larger than 100KB do not seem to display).

The iSpeech Translator is a free application found in the BlackBerry App World.  This free application features a speech to text converter, as well as translation to other languages both in text form and as speech.  The speech to text converter is very slow compared to the Xoom, and I think that I might observe a funny response if I actually used the translated text shown below (this is the same phrase that was supplied to the Xoom):

Unlike the Xoom, where it appears to be impossible to perform a screen capture without “rooting” the Xoom or using a cumbersome process that involves installing the Xoom developer kit, it is very easy to perform a screen capture on the PlayBook by holding the volume up and volume down buttons.  This screen capture capability is disabled when the BlackBerry Bridge is active, so shut off your BlackBerry phone if you have set up the BlackBerry Bridge:

The music player in the PlayBook works well.  Using the Xoom I bought several CDs worth of DRM free MP3s from Amazon’s Android portal.  Through a connected USB cable I was able to easily transfer the MP3s to a laptop using Windows Explorer, and then transfer the MP3s to the PlayBook also using Windows Explorer:

Incidentally, the USB and HDMI cables that work with the Xoom also work without problem with the PlayBook.

Unlike the Xoom, with its many applications, the applications for the PlayBook are currently very limited in number (and in some cases depth).  The PocketCloud application, with both VNC and Remote Desktop capability, does not exist for the PlayBook.  However, there is an interesting application that will talk to a VNC server on a Windows computer, and that application is capable of remotely controlling the Windows Media Center application on the Windows computer:

Simply add Internet TV to Windows Media Center, and you can use a BlackBerry PlayBook to change the channel to something more interesting (I might be implying something here).

One final interesting detail.  The Xoom is able to broadcast its MP3s to car receivers using BlueTooth – in this case the car is equiped with Microsoft Sync.  It is a slick feature, and will work even if the Xoom is turned off.  I have not tested to see if the BlackBerry PlayBook supports a similar feature.  A fun way to add a little Xoom to your vehicle without excessive visits to the fuel station:

—————————————————————————————————————-

(Section Added May 28, 2011)

Electronic Book Testing on the Xoom and PlayBook:

Acrobat Reader on the Xoom and PlayBook when showing the PDF eBook version of “Troubleshooting Oracle Performance” both accurately reproduced the printed version of the book.  However, on both tablets, when the pages were viewed in portrait orientation, the screen capture picture at the bottom of page 288 was hard to read due to antialiasing problems where the application simply removed lines of resolution.  The picture was readable in landscape orientation, however in that orientation only about 40% of the page was viewable without scrolling.  Only one page is visible on the screen at a time, although the pages can be stacked vertically end to end to produce a smoothly scrolling chain of pages (similar to the appearance on a desktop PC).

ezPDF Reader on the Xoom when showing the PDF eBook version of “Troubleshooting Oracle Performance” accurately reproduced the printed version of the book.  There were no antialiasing problems with the screen capture picture at the bottom of page 288 – the contents of that picture were easily readable in portrait orientation.  Only one page is visible on the screen at a time.

QuickOffice Pro HD on the Xoom when showing the PDF eBook version of “Troubleshooting Oracle Performance” accurately reproduced the printed version of the book.  There were no antialiasing problems with the screen capture picture at the bottom of page 288 – the contents of that picture were easily readable in portrait orientation.  Only one page is visible on the screen at a time, with pages stacked vertically end to end to produce a smoothly scrolling chain of pages (similar to the appearance on a desktop PC).

The ePUB format of the “Troubleshooting Oracle Performance” book has a file size roughly half as large as the PDF version of the same book.  I tested the Aldiko, FBReader, and CoolReader ePUB readers on the Xoom.  While all three of the ePUB viewers worked (Aldiko force closed a couple of times, usually when rotating the screen 90 degrees) the format reminded me of early HTML web pages with plain text between pictures.  None of the three ePUB viewers retained the original book page numbers, although it was still possible to jump from the index to section headings in the book.  All three viewers had difficulty with the monospaced Courier text areas, most noticeable in execution plans.  The Aldiko program came close when attempting to align the monospaced text correctly, although some of the execution plans still had formatting/alignment issues (alignment spaces were sometimes missing, and wide execution plans often line-wrapped).  The other two viewers used fonts that were not monospaced in those sections of the book, so the formatting/alignment issues often made the code sections impossible to read.

Acrobat Reader on the Xoom seems to lower the quality of images that are embedded in the text.  This was an obvious problem when looking at pages 112 and 114 of the “Expert Oracle Practices” book.

ezPDF Reader on the Xoom does well with the PDF version of the “Expert Oracle Practices” book.  The picture on page 112 is nearly as clear as it is in Acrobat reader on the PC when Acrobat Reader is zoomed to 134% – the picture in both cases is inferior to the printed copy of the picture as found in the book.  The picture on page 114 is very blurry on the PC and in ezPDF, but it is much more clear than it was in Acrobat Reader on the Xoom.

QuickOffice Pro HD failed to render all pictures that were more complicated than simple line drawings in the “Expert Oracle Practices” book.  The pictures on the front cover, pages 112, 114, 115, and several other pages were replaced with simple rectangles.  The line drawing on pages 30, 81, 82 appeared as expected.

Acrobat Reader on the PlayBook could not open the PDF version of the “Expert Oracle Practices” book because the PDF document is password protected as purchased from Apress.

Acrobat Reader, ezPDF Reader, and QuickOffice Pro HD on the Xoom all do well with the PDF version of the book “Expert One-On-One Oracle”.   The start of chapter picture on page 196, and the line drawings on pages 199 and 203 showed as clearly as is found in the printed book.  The code sections that had a light gray background in the book had a nearly invisible gray background in all three PDF viewing applications on the Xoom.  Acrobat Reader on the PlayBook also did well with this book, although the code sections had a much more visible light gray background that duplicated the effect found in the printed book (Acrobat Reader on the PC also produced the more visible light gray background).

Acrobat Reader displayed jagged edges on the circles found on page 18 of the “Oracle Performance Firefighting” book.  Zooming in on the circles greatly reduces the jagged edges, almost as if that forced the application to re-render the line drawing in a higher resolution.  The line drawings on pages 28, 30, 32 also exhibit jagged edges.

ezPDF Reader, and QuickOffice Pro HD on the Xoom exhibited no problems with the “Oracle Performance Firefighting” book – the line drawings were very smooth.

The “Oracle Performance Firefighting” PDF is password protected so it would not open in Acrobat Reader on the PlayBook.

The Google Books reader on the Xoom is very good, and seems to accurately reproduce printed books.  The application crashes when viewing the book “Beginning Oracle SQL” while attempting to display pages 56 and 57 side-by-side – of course the application tries to reopen these two pages when the book is selected, so an endless loop of crashes is experienced every time the book is opened until the tablet is rotated 90 degrees.





Adding Features to PowerPoint Based Oracle Presentation Files

28 04 2011

April 28, 2011

This blog article is not specific to Oracle Database, but I thought that I would share the concepts anyway.  If you are giving presentations and using PowerPoint, consider including detailed notes sections in the presentation.  What benefit do those notes sections serve?  Well, if you share the presentation files with the viewers of the presentation, those note sections act as a reminder of what was stated during your presentation.  In fact, you could go so far as to type everything that you intend to say during the presentation into the presentation notes section for the slides.

Let’s take a look at a couple of interesting features that can be implemented in a PowerPoint presentation when detailed notes sections are provided.

Read to Me:

In 1998 (or maybe it was 1999) I experimented with the Microsoft Speech API, which at that time was still in Beta form for the initial release.  More recent releases of the Microsoft Speech API are obviously much more sophisticated, but at the time it was possible to easily change between one of several “voices” with different pitch and speed settings.  It is very easy to incorporate speech capabilities into a PowerPoint presentation, because the Speech API is installed by default on computers running Microsoft Office (I believe that the Speech API is also included in Windows operating systems starting with Microsoft Vista).  A very simple, generic PowerPoint macro may be used to read back the notes section of the currently displayed slide:

Sub SpeakNotes()
    Const SVSFlagsAsync = 1
    Const SVSFPurgeBeforeSpeak = 2
    'Dim strText As String
    Dim strSpeech As String
    Dim objSpeech As Object
    Dim lngCurrentSlide As Long

    On Error Resume Next

    Set objSpeech = CreateObject("SAPI.SpVoice")

    lngCurrentSlide = SlideShowWindows(1).View.CurrentShowPosition

    If Application.Version <= "11.0" Then
        strSpeech = ActivePresentation.Slides(lngCurrentSlide).NotesPage.Shapes.Placeholders(2).TextFrame.TextRange.Text
        'Change the pitch
        'strSpeech = "<pitch middle='25'>" & ActivePresentation.Slides(lngCurrentSlide).NotesPage.Shapes.Placeholders(2).TextFrame.TextRange.Text
    Else
        strSpeech = ActivePresentation.Slides(lngCurrentSlide).NotesPage.Shapes.Placeholders(2).TextFrame.TextRange.Text
    End If

    objSpeech.Speak strSpeech

    Set objSpeech = Nothing
End Sub

In the above you will notice that the macro code checks the version of PowerPoint so that it can potentially run a different set of control commands for the speech API (I do not recall the exact reason why I included this years ago, but I believe it is because the default voice in Microsoft Office 2003 is a male voice, while the default voice in Microsoft Office 2007 is a female voice).  Now all that needs to be done is to create a picture or object of some sort on a slide and associate an action with the object that executes the above macro.  I have used a couple of different objects over the years, typically designed to clearly communicate what will happen when the object is clicked, for example:

Write to Me:

Another interesting feature that may be implemented is exporting the slides to JPG pictures, and then building a Microsoft Word Document from the exported JPG pictures and the slide notes – this is helpful for both the presenter and the people learning from the presentation.  In the past I had to manually create these types of handouts, so I thought “why not automate the process?”

We will start with the code to generate the JPG pictures from the presentation slides:

 
Sub WriteSlidestoJPG()
    On Error Resume Next

    'Create a folder for the slides if one does not already exist
    If Len(Dir("C:\Presentation Slides", vbDirectory)) < 4 Then
        MkDir "C:\Presentation Slides"
    End If

    'Remove any slides from a previous execution
    Kill "C:\Presentation Slides\*.*"
    'Save the slides as JPG pictures
    ActivePresentation.Export "C:\Presentation Slides", "JPG", 640, 480  '640 pixels by 480 pixels
End Sub

Next, we will add a second macro that builds the Microsoft Word document:

Sub SendPowerPointSlidestoWord()
    Dim i As Integer
    Dim objWord As Word.Application

    On Error Resume Next

    Set objWord = New Word.Application

    If Err = 0 Then
        WriteSlidestoJPG

        With objWord
            .Documents.Add
            .Visible = True
            With .ActiveDocument.Styles(wdStyleNormal).Font
                If .NameFarEast = .NameAscii Then
                    .NameAscii = ""
                End If
                .NameFarEast = ""
            End With
            With .ActiveDocument.PageSetup
                .TopMargin = InchesToPoints(0.5)
                .BottomMargin = InchesToPoints(0.5)
                .LeftMargin = InchesToPoints(0.75)
                .RightMargin = InchesToPoints(0.25)
                .HeaderDistance = InchesToPoints(0.25)
                .FooterDistance = InchesToPoints(0.25)
            End With

            If .ActiveWindow.View.SplitSpecial <> wdPaneNone Then
                .ActiveWindow.Panes(2).Close
            End If
            .ActiveWindow.ActivePane.View.Type = wdPrintView
            .ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
            .Selection.Style = .ActiveDocument.Styles("Heading 1")
            .Selection.TypeText Text:=Left(ActivePresentation.Name, InStrRev(ActivePresentation.Name, ".") - 1)
            .Selection.TypeText Text:="   by " & ActivePresentation.BuiltInDocumentProperties.Item("author").Value
            .ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageFooter
            .Selection.ParagraphFormat.TabStops(InchesToPoints(6)).Position = InchesToPoints(7.5)
            .Selection.TypeText Text:=vbTab & vbTab & "Page "
            .Selection.Fields.Add Range:=.Selection.Range, Type:=wdFieldPage
            .Selection.TypeText Text:=" of "
            .Selection.Fields.Add Range:=.Selection.Range, Type:=wdFieldNumPages
            .ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument

            .Selection.MoveLeft Unit:=wdCharacter, Count:=2

            .ActiveDocument.Tables.Add Range:=.Selection.Range, NumRows:=ActivePresentation.Slides.Count, NumColumns _
                :=2, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
                wdAutoFitFixed
            With .Selection.Tables(1)
                .Columns.PreferredWidth = InchesToPoints(7.5)
            End With
            With .Selection.Tables(1)
                .TopPadding = InchesToPoints(0)
                .BottomPadding = InchesToPoints(0)
                .LeftPadding = InchesToPoints(0.08)
                .RightPadding = InchesToPoints(0.08)
                .Spacing = 0
                .AllowPageBreaks = True
                .AllowAutoFit = False
            End With
            .Selection.Tables(1).Columns(1).PreferredWidthType = wdPreferredWidthPoints
            .Selection.Tables(1).Columns(1).PreferredWidth = InchesToPoints(3)
            .Selection.Move Unit:=wdColumn, Count:=1
            .Selection.SelectColumn
            .Selection.Columns.PreferredWidthType = wdPreferredWidthPoints
            .Selection.Columns.PreferredWidth = InchesToPoints(4.5)

            .Selection.MoveLeft Unit:=wdCharacter, Count:=2

            For i = 1 To ActivePresentation.Slides.Count
                .Selection.InlineShapes.AddPicture FileName:="C:\Presentation Slides\Slide" & Format(i) & ".JPG", LinkToFile:=False, SaveWithDocument:=True
                .Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
                .Selection.InlineShapes(1).LockAspectRatio = msoTrue
                .Selection.InlineShapes(1).Width = 203.05
                .Selection.InlineShapes(1).Height = 152.65
                .Selection.MoveRight Unit:=wdCharacter, Count:=2
                With .Selection.Font
                    .Name = "Times New Roman"
                    .Size = 8
                    .Bold = False
                End With
                .Selection.TypeText Text:=ActivePresentation.Slides(i).NotesPage.Shapes.Placeholders(2).TextFrame.TextRange.Text
                .Selection.MoveDown Unit:=wdLine, Count:=1
                .Selection.MoveLeft Unit:=wdCharacter, Count:=1
            Next i
        End With
    End If
    Set objWord = Nothing
End Sub

—————

Anyone else have additional ideas for adding features to PowerPoint based Oracle Presentation Files?





On the Topic of Technology… 2

11 04 2011

April 11, 2011

(Back to the Previous Post in the Series) (Forward to the Next Post in the Series)

Roughly a year ago I wrote a blog article that described the process that I went through, wandering around a big chain electronics store in pursuit of an iPad.  I first wandered past the netbooks, and if that had not happened, I probably would have walked out of the store with an iPad rather than a Toshiba netbook.  The netbook still works reasonably well for those times when I need an ultra-portable full featured computer – however, I more than doubled the value of the netbook by installing 2GB of memory, upgrading Windows 7 Starter to Windows 7 Professional, and installing Microsoft Office 2010.

Well, nearly a year later with the launch of the iPad 2 underway, I again went for a walk through another big chain electronics store in search of an interesting electronics gadget.  I found the iPad 2 display and experimented with one of the two units that was on display.  My first reaction was a remark about the snazzy screen effects when opening applications – an impressive feature.  The iPad 2 reminded me a lot of the iPad that I experimented with a year earlier – it is a media consumption device.  Since I still have a Verizon wireless plan for Internet access, with steep overage charges, an Internet media consumption device would still have limited usefulness for me.  Still no Flash support, so that would limit its usefulness of accessing the Flash based sites (such as the Oracle support site) on the iPad 2.

I wandered around the electronics store a bit more and found another tablet tucked away in the corner with a couple of laptops – a Motorola Xoom with 3G wireless.  My first reaction to the less than snazzy screen effects (compared to the iPad 2) was simply – is that all there is?  I experimented with the tablet for a couple of minutes.  Neat satellite view of the store on the little tablet, and wow is it fast updating the screen.  Moderately neat book application.  Sweeping my finger across the screen… that’s kind of neat – I can’t do that on the netbook (the netbook and Xoom have similar screen dimensions and aspect ratio).  Viewing web pages seems to work, even if the websites deliver the mobile versions of the web pages.  Interesting, or as useless as the iPad 2 for what I would do with it?  I left the store empty handed.

I did a bit more research on the Motorola Xoom tablet.  It seems that not only does the Xoom have a 5 megapixel camera on its backside that records 720p video, but it also has a high resolution forward facing camera.  Oh, it supports Flash also, and is apparently the particular tablet model that Google used during devlopment of the new Android 3 (Honeycomb) operating system.  The information that I had found on the Internet suggested that the Xoom was less of a media consumption device than an iPad 2, and more of a content producer device.  Interesting, but can it run Oracle Database?

Considering that flyers and articles about various tablets (iPad, Zoom, BlackBerry, Dell, etc.)  were stacking up on my desk at work, I thought that I would try an experiment:

Zooming in on the center of the above picture, we see that the screen on the Xoom is the My Oracle Support Flash-based site.  As of yet I have not determined how to display the on-screen keyboard on demand, so I can’t yet type into the text entry fields:

Flash works, and I am able to see the non-mobile, non-limited versions of websites.  So, what is the killer application for the Xoom?  Well, it is hard to beat a talking cat for $1.00:

So, the question again, can it run Oracle?  Well, I think that this picture answers that question (click the piture to zoom in on the guy who is waving from the boat):

If it were not for the crashing applications including the Android Apps/Books Market and SpeedView (uses the Xoom’s built-in GPS to display various vehicle speed statistics), the device would be nearly perfect as a  productivity tool that also is able to consume media (the TuneIn Radio application is quite handy, and with an HDMI cable plugged into a 7.1 receiver, it sounds very good).  The QuickOffice application, at $15, is certainly less expensive than Office 2010 that is installed on the netbook; the Power Point document editor in QuickOffice seems to have difficulty with complexly formatted Power Point files, but it works OK as a viewer (as long as there is no animation).  The PDF viewer that is part of QuickOffice works, but it is not great.  No problem, just a couple more dollars for the ezPDF reader and I am able to view various PDF books and the Oracle documentation library.  Attaching a $12 HDMI cable will even allow the screen to be quickly projected onto the TV, while attaching the included USB cable allows organizing the various files loaded onto the Xoom into folders and to easily rename those files.

So, is it worth $600?  If you need a high definition video camera, a 5 mega-pixel camera, a GPS, easy/fast speech recognition, inexpensive applications, the ability to view just about any web page in a light-weight, mobile package, then the Xoom just might be worth $600.  If you have no use for those features, then the Xoom will make an excellent paperweight to keep the tablet flyers and news articles from blowing off the desktop.





A Year Older, Any Wiser?

30 11 2010

November 30, 2010 (Updated December 1, 2010)

(Forward to the Next Post in the Series)

It feels a bit strange, but this blog went live a year ago today.  What was the motivation for creating the blog?  At the time I was comtemplating creating a publically accessible Oracle Database related blog, I already had a restricted-access Oracle blog, and if I was lucky I had maybe four regular visitors (I might have five regular visitors now, or two that hit the refresh button 2.5 times a day).  Before setting up this blog I made the following comment in the above linked OTN thread:

“Yes, a lot of time is spent putting together the test cases. Even more time would be required to put together blog articles that better describe what appears in the test. I do not know if I am able to justify the time involvement, but I will consider starting a blog. However, there are already a large number of very good Oracle blogs. Maybe if it were used just to preserve some of the test cases?”

I had commented something like this, in a previous OTN thread when someone suggested that I set up a blog:

There are already a large number of fantastic Oracle related blogs on the Internet, and every subtopic seemed to have been previously covered in great detail.  I could not image developing unique material that anyone would care to read.

I guess that I proved myself wrong, yet again.  I can think of five good articles in the last year that I published, although I might be imagining things.  So, for the five regular readers (or the 2 that hit the refresh button 2.5 times daily), what are the five good articles?

Wow, I spent (maybe wasted, had I not learned a lot in the process) a lot of time in the last year creating material for this blog.

——————-

Edit: December 1, 2010:

Some of the 400 to 800 non-regular (thus, those who want to know why something is as it is) visitors to this blog who visit daily may be wondering what the symbol means at the top right of my blog, and where I obtained the symbol.  I will leave the meaning of the symbol to your imagination.  So, where did I obtain the symbol?  It was created using a painting/imaging editing program that I created in the late 1990s.  Take a look:

The splash screen, created entirely in the program:

The starting point, not much unusual here:

Let’s use some of those mathematics abilities that the teachers said were so critical to know in life, let’s build a brush using a cosine wave:

Better yet, let’s use sine wave for the brush and change the colors a bit:

Finger painting with the new brush – looking good, except that I keep scratching the canvas, leaving behind black marks:

Let’s play with some more mathematics to transform the picture:

We will use a spiral wave which progressively twists the image the further out from the center you go – using the angular coordinate system and sine and cosine to translate back into X, Y coordinates:

Let’s crank up the twist a bit more and see what it looks like – that certainly looks better than the previous pictures (I fixed the scratches in the canvas first):

But the earlier picture is still too plain, let’s add some lighting effects using, you guessed it, more mathematics:

That lower right corner looks a little boring, let’s roll it up using more mathematics:

Let’s do the same for the top left corner – that’s what mathematics are good for, rolling up the rug:

The last four pictures – pretty neat what can be done with all the mathematics we have worked so hard to forget over the years, too bad I never did anything useful with the painting/image editing program other than to use it as a teaching tool:

 





On the Topic of Technology…

4 05 2010

May 4, 2010

(Forward to the Next Post in the Series)

Today’s post seems slightly off topic from previous posts, but maybe there is a hidden message.

I have in my basement an ancient Commodore Amiga 1000 computer which first hit the market in 1985.  At the time of its initial release the computer offered an earth shattering feature set: 7.14MHz Motorola 65000 CPU, 4096 colors, stereo sound, speech synthesis, and preemptive multitasking.  It’s true that the computer is rather tame by comparison with current computers, 25 years after the initial introduction of the Amiga 1000.  Before Commodore’s eventual demise, I was very much a fan of their product line.

So, why bring up ancient history about a 25 year old computer from a long ago defunct computer company?  This past Saturday I stopped into one of the electronic stores on my way home from an OakTable planning meeting, with the intention of buying one of the Apple iPad devices.  From a personal finance position, I planned to consider the purchase as an educational expenditure, not much different from the justification that I use to spend a portion of my income on various books and magazines.  Let’s see… $500 USD for a unit with 16GB or $700 USD for a unit with 64GB – I figured that I would probably need to buy the $700 unit if I planned to do anything useful with the device since it cannot be upgraded at a later time.  An expensive learning exercise, no doubt, but I already have a dozen or so newspaper, magazine, and Internet articles sitting on my desk at work about the iPad, most of which have been brought to me by others in the company who were curious about the iPad.

Back to the story – as I was walking toward the iPads in the electronics store I walked past 8 netbooks that were running Windows 7 Starter Edition.  The most expensive netbook offered a 1.66GHz Intel Atom processor (with hyperthreading support), 1GB of memory, 250GB hard drive, 10.2 inch LCD, an 11 hour battery, built-in web camera, and weighed 2.5 pounds – for $400 USD (I later determined that the same netbook is available online for $360).  I found that it was interesting that the netbook was able to cold boot in about 35 seconds, and resume from sleep mode in about 2 seconds.  After a couple of minutes experimenting with the netbooks I wandered over to the Apple display.  On the way I started wondering what I would do with the iPad, since I own a decent 3 year old laptop with a high-resolution 17 inch LCD.  I found one of the unoccupied iPads at the Apple display counter and started experimenting with it.

Wow, neat visual effects, jumping from the main screen into the mapping application.  OK, now how do I switch back to the main screen and pull up a web browser?  The iPad does not offer multitasking, even though that 25 year old Amiga 1000 computer sitting in my basement does?  True, but I knew that before entering the store.  After hopelessly pressing on the screen to find the hidden “close” button for the mapping application, I found a button on the frame that surrounded the screen – surprisingly, that button closed the mapping application.  Interesting, apparently no button on the main screen to open a web browser (the feature was probably disabled on the display unit), but there is a button for YouTube.  Wow, neat visual effects when pointing at YouTube.  What should I search for?  How about “Oracle”.  I brought up the onscreen keyboard, set the unit down on the display counter so that I could effectively type on the screen, and found that this motion caused the screen to rotate 90 degrees.  Neat visual effect… it was about this time that I noticed that the iPad screen was also roughly 10 inches measured diagonally, much like those netbooks that I passed on the way over to the Apple display.

The iPad played the selected YouTube video without problem, and did I mention the neat visual effects.  I started thinking again about how I would use the iPad.  I could use it to watch videos, as long as the videos were not Flash based.  My broadband connection has either a 5GB or 6GB monthly maximum transfer limit as it is provided by a cell phone company, of course I could easily hit that limit if the iPad was only used to watch videos.  All I have to do is plug the USB modem into the USB port on the iPAD… of course I mean plug the USB modem into the CradlePoint wireless router – there are no USB ports on the iPad.  I could use it for viewing the Oracle documentation, assuming that the Adobe’s Acrobat Reader format is not banned from the iPad.  While I don’t think that it will ever be possible to run a copy of Oracle Database on an iPad, I could use it to look up information on Metalink (My Oracle Support)… on second thought, who made the decision to build the new Metalink site using Flash – didn’t that person consult Steve Jobs?  I started to wonder again why I was planning to buy one of these iPads – for my purposes the only thing that it really had going for it was the neat visual effects, the mapping application, and the semi-portability (I have to drag along the CradlePoint router and external antenna).

I wandered back over to the netbooks.  Let’s see, these miniature sized computers offer four times as much storage space as the most expensive iPad, check; 66% faster CPU than the most expensive iPad, check; keyboard does not rotate 90 degrees when placed on the table, check; same screen size as the iPad, check; same weight as the iPad, check; multiple (3) USB ports, check; no fingerprints on the screen/case, check; 11 hour battery life, check; built-in web cam (probably coming to the iPads in December), check; able to visit the Flash enabled My Oracle Support site, check; able to run most of the software that I own, check; most expensive unit is $100 cheaper than the least expensive iPad, check; able to watch most video formats, with the notable exception of MPEG2 (as recorded by a Tivo), check; able to multitask just like the 25 year old Amiga, check; able to run Oracle Database – certainly that can’t be possible on a netbook, right?

So, I walked out of the store with a bag in hand, mission accomplished.  That certainly is a strange looking iPad – how did Apple manage to sell a million of them in a month?  Forward progress still means moving forward, right?

Using 40% of the 1GB of memory, just sitting idle.

I thought about installing Oracle on the little device, but as you can see, the books were just too heavy.

It is far too small, too limited of a device to support Oracle Database 11g R2.

Definitely, the hard copies are far too heavy.

This blog article is not intended to imply that the technology of the iPad is behind that of the 25 year old Commodore Amiga due to the iPad’s lack of multitasking.  This blog article is not intended to insult the owners of the 1 million iPads that were sold in the last month, even those that ended up in a blender or were smashed on the concrete as demonstrations of the rugged design of the iPads.  This blog article is not intended to point out how limited an iPad might be to someone wanting to perform serious work on the iPad – if you consider navigating the Flash enabled My Oracle Support site serious work.  No, this blog article is intended to point out that it is hard to install Oracle Database on a device without a physical keyboard.  See, the blog article was on topic after all.  🙂

I think that humor somehow found its way into this blog article.  I wonder if this article about the iPad will end up on my desk with the other assorted iPad articles?