The Art of C Programming

##1. Principle

  • Array is NOT point
  • Avoid using unsafe api
  • Deeply understand point

##2. math

2.1 mod

if n%2k=x, then x=n&(2k-1)

2.2 even odd check

if the lowest bit of binary mode is 0, even number, otherwise odd.

2.3 multi

a*5 = a<<2+a

##3.structure

3.1 size

int arr[] = {1, 5, 3, 18, 107, 21, 88};

size of arr is sizeof(arr)/sizeof(arr[0])

struct _test_ {
    int a;
    double b;
    char c;
}

size of _test_ is sizeof(_test_)

##4. Puzzle

4.1 Print the numbers between 1 and 1000.

  • Add a rule: Don’t use any loops.
  • Add another rule: No recursions.
  • We can’t do that? OK. Please look at this one.
#include "stdio.h"
#define A(x) x;x;x;x;x;x;x;x;x;x;
int main (void){
  int n = 1;
  A(A(A(printf ("%d ", n++))));
  return 0;
}

4.2 swap two variables

a = a^b;
b = a^b;
a = a^b;

##5. What we need to avoid using?

  • gets –> fgets
  • scanf –> scanf_s sscanf
  • sprintf –> snprintf sprintf_s
  • strcpy –> strlcpy
  • strcat –> strlcat
  • strtok –> strtok_r

##6. v[index] and index[v] are the same

##7. use _Generic macro

#include <stdio.h>
#include <stdlib.h>

void printchar(char X) { printf("char: %c\n", X); }
void printint(int X) { printf("int: %d\n", X); }
void printfloat(float X) { printf("float: %f\n", X); }
void printdouble(double X) { printf("double: %lf\n", X); }
#define print(X) _Generic((X), \
                               char: printchar, \
                               int: printint, \
                               float: printfloat, \
                               double: printdouble)(X)

int main()
{
    print(1);
    print(1.2);
    print((char)'a');
    return 0;
}

GCC version don’t support _Generic macro currently, but clang work.
Compile this code with clang as clang test.c

##8. Reference

windows event log

1. Introduce

Application can use the Event logging API to report and view events.
For details on reporting events, see Reporting Events.
To view events that have been written to log files, see Querying for Event Source Messages
and Receiving Event Notification.

You can find widows event log files(binary file) on C:\WINDOWS\system32\config\, list as below:

C:\WINDOWS\system32\config\AppEvent.Evt
C:\WINDOWS\system32\config\SecEvent.Evt
C:\WINDOWS\system32\config\SysEvent.Evt

2. Program

2.1 Prepare work

2.1.1 Write a message text file, name provider.mc, the follow show it

; // MyEventProvider.mc 

; // This is the header section.


SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
               Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
               Warning=0x2:STATUS_SEVERITY_WARNING
               Error=0x3:STATUS_SEVERITY_ERROR
              )


FacilityNames=(System=0x0:FACILITY_SYSTEM
               Runtime=0x2:FACILITY_RUNTIME
               Stubs=0x3:FACILITY_STUBS
               Io=0x4:FACILITY_IO_ERROR_CODE
              )

LanguageNames=(English=0x409:MSG00409)


; // The following are the categories of events.

MessageIdTypedef=WORD

MessageId=0x1
SymbolicName=NETWORK_CATEGORY
Language=English
Network Events
.

MessageId=0x2
SymbolicName=DATABASE_CATEGORY
Language=English
Database Events
.

MessageId=0x3
SymbolicName=UI_CATEGORY
Language=English
UI Events
.


; // The following are the message definitions.

MessageIdTypedef=DWORD

MessageId=0x100
Severity=Error
Facility=Runtime
SymbolicName=MSG_INVALID_COMMAND
Language=English
The command is not valid.
.


MessageId=0x101
Severity=Error
Facility=System
SymbolicName=MSG_BAD_FILE_CONTENTS
Language=English
File %1 contains content that is not valid.
.

MessageId=0x102
Severity=Warning
Facility=System
SymbolicName=MSG_RETRIES
Language=English
There have been %1 retries with %2 success! Disconnect from
the server and try again later.
.

MessageId=0x103
Severity=Informational
Facility=System
SymbolicName=MSG_COMPUTE_CONVERSION
Language=English
%1 %%4096 = %2 %%4097. 
.


; // The following are the parameter strings */


MessageId=0x1000
Severity=Success
Facility=System
SymbolicName=QUARTS_UNITS
Language=English
quarts%0
.

MessageId=0x1001
Severity=Success
Facility=System
SymbolicName=GALLONS_UNITS
Language=English
gallons%0
.

Use the following command to compile the message text file:

mc -U provider.mc

It will generate 3 files: (the report event project use provider.h)

provider.h
provider.rc
MSG0409.bin

Compile the resources file:

rc provider.rc

Generate only one file, provider.res

Last use link to create resource-dll file:

link -dll -noentry provider.res

Output file, provider.dll, with these library, event log viewer can show the
the detail description by ID, Type … etc.(at the follow, Receiving event
notification use thid library)

2.2 Report event log

An example show how to report event.
Create a win32 console project, with empty file.
Then, add a reportevent.cpp to it, and add the follow code.

#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
#include "provider.h"

#pragma comment(lib, "advapi32.lib")

#define PROVIDER_NAME L"MyEventProvider"

// Hardcoded insert string for the event messages.
CONST LPWSTR pBadCommand = L"The command that was not valid";
CONST LPWSTR pFilename = L"c:\\folder\\file.ext";
CONST LPWSTR pNumberOfRetries = L"3";
CONST LPWSTR pSuccessfulRetries = L"0";
CONST LPWSTR pQuarts = L"8";
CONST LPWSTR pGallons = L"2";

void wmain(void)
{
    HANDLE hEventLog = NULL;
    LPWSTR pInsertStrings[2] = {NULL, NULL};
    DWORD dwEventDataSize = 0;

    // The source name (provider) must exist as a subkey of Application.
    hEventLog = RegisterEventSource(NULL, PROVIDER_NAME);
    if (NULL == hEventLog)
    {
        wprintf(L"RegisterEventSource failed with 0x%x.\n", GetLastError());
        goto cleanup;
    }

    // This event includes user-defined data as part of the event. The event message
    // does not use insert strings.
    dwEventDataSize = ((DWORD)wcslen(pBadCommand) + 1) * sizeof(WCHAR);
    if (!ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, UI_CATEGORY, MSG_INVALID_COMMAND, NULL, 0, dwEventDataSize, NULL, pBadCommand))
    {
        wprintf(L"ReportEvent failed with 0x%x for event 0x%x.\n", GetLastError(), MSG_INVALID_COMMAND);
        goto cleanup;
    }

    // This event uses insert strings.
    pInsertStrings[0] = pFilename;
    if (!ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, DATABASE_CATEGORY, MSG_BAD_FILE_CONTENTS, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL))
    {
        wprintf(L"ReportEvent failed with 0x%x for event 0x%x.\n", GetLastError(), MSG_BAD_FILE_CONTENTS);
        goto cleanup;
    }

    // This event uses insert strings.
    pInsertStrings[0] = pNumberOfRetries;
    pInsertStrings[1] = pSuccessfulRetries;
    if (!ReportEvent(hEventLog, EVENTLOG_WARNING_TYPE, NETWORK_CATEGORY, MSG_RETRIES, NULL, 2, 0, (LPCWSTR*)pInsertStrings, NULL))
    {
        wprintf(L"ReportEvent failed with 0x%x for event 0x%x.\n", GetLastError(), MSG_RETRIES);
        goto cleanup;
    }

    // This event uses insert strings.
    pInsertStrings[0] = pQuarts;
    pInsertStrings[1] = pGallons;
    if (!ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, UI_CATEGORY, MSG_COMPUTE_CONVERSION, NULL, 2, 0, (LPCWSTR*)pInsertStrings, NULL))
    {
        wprintf(L"ReportEvent failed with 0x%x for event 0x%x.\n", GetLastError(), MSG_COMPUTE_CONVERSION);
        goto cleanup;
    }

    wprintf(L"All events successfully reported.\n");

cleanup:

    if (hEventLog)
        DeregisterEventSource(hEventLog);
}

2.3 View event log

go to http://msdn.microsoft.com/en-us/library/aa363677(v=vs.85).aspx
get the source code of “Receiving Event Notification”.

Notice that, change these two lines for your apply:

#define PROVIDER_NAME L"MyEventProvider"
#define RESOURCE_DLL  L"<path>\\Provider.dll"

3. Questions

How to monitor multi event logs? And make it to be a component.
I want to implement it, on this site you can find a first version project
Click here

But I think, it was a pool method to check each event name. Is there exist any
good method to do it? I meet the same problem with this guy,
Unexpected behavior of WaitForMultipleObjects for events used in NotifyChangeEventLog
I’m sad, why he don’t told the detail info.

4. Reference

network client server model

##Some words before
On this paper, I want to tell a story of network client server model. I would
do it on the coming weeks(^_^). Let’s waiting…

##Reference:

Do you really master c language

##1. Array

  1. Two dimensional array:
    If you have an 2-d array like char buf[50][255] = {0};, and use it for function

    void test(char *buf[])
    {

    printf("buf[0] = %s\n", buf[0]);
    

    }

It would appear warning as below:

[dennis@localhost test]$ gcc -o t t.c
t.c: In function ‘main’:
t.c:179:2: warning: passing argument 1 of ‘test’ from incompatible pointer type [enabled by default]
t.c:146:6: note: expected ‘char **’ but argument is of type ‘char (*)[255]’

The correct function format is : void test(char (*buf)[255])

  1. Array initial:

    / This code is from strace’s source code /
    / strace is tool use for trace all the system call of linux binary /
    #include
    #include

    #define PTRACE_EVENT_CLONE 3
    #define PTRACE_EVENT_FORK 1
    #define PTRACE_EVENT_VFORK 2
    #define PTRACE_EVENT_VFORK_DONE 5
    #define PTRACE_EVENT_EXEC 4
    #define PTRACE_EVENT_EXIT 6

    #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

    int main(int argc, char* argv[])
    {

    static const char *const event_names[] = {
        [PTRACE_EVENT_CLONE] = "CLONE",
        [PTRACE_EVENT_FORK]  = "FORK",
        [PTRACE_EVENT_VFORK] = "VFORK",
        [PTRACE_EVENT_VFORK_DONE] = "VFORK_DONE",
        [PTRACE_EVENT_EXEC]  = "EXEC",
        [PTRACE_EVENT_EXIT]  = "EXIT",
    };
    
    int i=0; 
    for (i=0; i<ARRAY_SIZE(event_names); i++)
        printf("event_name[%d] = %s\n", i, event_names[i]);
    return 0;
    

    }

  2. Arrays of Length Zero

    #include
    #include

    struct line {

    int length;
    char contents[0]; // C99的玩法是:char contents[]; 没有指定数组长度
    

    };

    int main(){

    int this_length=10;
    struct line *thisline = (struct line *)
                     malloc (sizeof (struct line) + this_length);
    thisline->length = this_length;
    memset(thisline->contents, 'a', this_length);
    free(thisline);
    return 0;
    

    }

##2. point

2.1 void test(char *a)

if want to change the value, use this.

2.2 void test(char **a)

if want to allocate memory, use this.

##3. structure

3.1 initial by bit

struct pid_tag {
    unsigned int inactive : 1;
    unsigned int          : 1; /* fill 1 bit */
    unsigned int refcount : 6;
    unsigned int          : 0; /* fill to next word */
    short pid_id;
    struct pid_tag *link;
}

3.2 alignment

struct st_a {
    int a;
    char b;
    short c;
};

struct st_b {
    char b;
    short c;
    int a;
};

struct st_c {
    char b;
    int a;
    short c;
};

the value of sizeof(st_a) is 8  (address: 0 1 2 3 | 4 . | 6 7)
the value of sizeof(st_b) is 8  (address: 0 . | 2 3 | 4 5 6 7)
the value of sizeof(st_c) is 12 (address: 0 . . . | 4 5 6 7 | 8 9 . .)

Therefore, you should careful for doing structure copy, and socket programming.

##4. Function

For more :

my favour website

Read

math

blog_en

blog_cn

news

site

edu

blog

IQ

music

photo

site

cycling

node.js

Install Node.js

wget http://nodejs.org/dist/v0.10.5/node-v0.10.5.tar.gz

./configure
make
make Install

Create a http webserver

Awesome tools use node

pdftest.js (run command node pdftest.js)

var PDFDocument = require('pdfkit'), 
doc = new PDFDocument();

doc.moveTo(300, 75)
   .lineTo(373, 301)
   .lineTo(181, 161)
   .lineTo(419, 161)
   .lineTo(227, 301)
   .fill('red', 'even-odd'); 

var loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam in...';

doc.y = 320;
doc.fillColor('black')
   .text(loremIpsum, {
       paragraphGap: 10,
       indent: 20,
       align: 'justify',
       columns: 2
   });

doc.write('out.pdf');

Referece

net snmp

1.Architecture

1.1 Agent Architecture

Upon starting, the agent goes through the following steps (in SnmpDaemonMain()):

  • reads command line options
  • decides whether it’s a master or subagent
  • calls init_agent()
    • initializes the mib-module registration tree
    • registers its own configuration file tokens and callbacks
    • initializes the Agent Helpers
  • initializes all the compiled-in mib modules
  • initializes the base libnetsnmp library
  • opens all the required ports to listen on
  • forks
  • saves persistent data (it’s likely at least something has changed already)
  • sends a coldStart trap
  • invokes receive() to perform the main packet handling

Reference: Agent_Architecture

1.2 snmpget snmpset snmpwalk snmptrap

Reference: mib2c_General_Overview

* snmptranslate: learning about the MIB tree.
* snmpget:       retrieving data from a host.
* snmpgetnext:   retrieving unknown indexed data.
* snmpwalk:      retrieving lots of data at once!
* snmptable:     displaying a table.
* snmpset:       peforming write operations.
* snmpbulkget:   communicates with a network entity using SNMP GETBULK request
* snmpbulkwalk:  retrieve a sub-tree of management values using SNMP GETBULK requests.
* snmptrap:      sending traps messages, and acting upon them.
* snmptrapd:     receive and logs SNMP TRAP and INFORM messages.  

for more, use 'man' command to see usage.

example:

#### snmpwalk
[dennis@localhost ~]$ snmpwalk -v2c -c public 192.168.110.98 .1.3.6.1.4.1.50369
...
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.3.1 = INTEGER: 856398
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.4.1 = INTEGER: 432164
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.5.1 = INTEGER: 424234
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.6.1 = INTEGER: 1
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.7.1 = INTEGER: 3
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.8.1 = STRING: "0:1 0:2 0:3 "
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.9.1 = INTEGER: 3
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.10.1 = INTEGER: 0
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.11.1 = INTEGER: 4
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.12.1 = INTEGER: 0
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.13.1 = INTEGER: 5

#### snmpget
[dennis@localhost ~]$ snmpget -v2c -c public 192.168.110.98 .1.3.6.1.4.1.50369.1.1.2.5.1.3.1
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.3.1 = INTEGER: 856398

[dennis@localhost ~]$ snmpget -v2c -c public 192.168.110.98 .1.3.6.1.4.1.50369.1.1.2.5.1.8.1
SNMPv2-SMI::enterprises.50369.1.1.2.5.1.8.1 = STRING: "0:1 0:2 0:3 "

#### snmpset
[dennis@localhost ~]$ snmpset -v2c -c private 192.168.110.98 .1.3.6.1.4.1.50369.1.1.1.3.1.0 i 2
SNMPv2-SMI::enterprises.50369.1.1.1.3.1.0 = INTEGER: 2

[dennis@localhost ~]$ snmpset -v2c -c private 192.168.110.98 .1.3.6.1.4.1.50369.1.1.1.3.2.0 s "2013-08-16 15:32:40"
SNMPv2-SMI::enterprises.50369.1.1.1.3.2.0 = STRING: "2013-08-16 15:32:40"

#### snmptrap
# use snmptrapd to receive trap message.
# 1.first create file "touch /usr/local/share/snmp/snmptrapd.conf" 
# 2.then add "authcommunity execute,log,net public" to the file to access 
#   receive trap message.
# (use command 'snmpconf' to create and config is another choise)
# use root to start snmptrapd 
[root@localhost ~]# snmptrapd -f -Le -F "%02.2h:%02.2j TRAP%w.%q from %b\n %v\n"
NET-SNMP version 5.7.2
14:30 TRAP0.0 from UDP: [192.168.110.39]:52222->[192.168.50.63]:162
 DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (1) 0:00:00.01    SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.50369.1.3.0.1    SNMPv2-SMI::enterprises.50369.1.1.2 = INTEGER: 1
15:38 TRAP0.0 from UDP: [172.16.110.39]:35505->[172.16.50.63]:162
 SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.50369    SNMPv2-SMI::enterprises.50369.1.1.2 = STRING: "string value test"    

root:~# snmptrap -v 2c -c public 192.168.50.63 1 .1.3.6.1.4.1.50369.1.3.0.1 .1.3.6.1.4.1.50369.1.1.2 i 1
root:~# snmptrap -v 2c -c public 192.168.50.63 "abc" .1.3.6.1.4.1.50369 .1.3.6.1.4.1.50369.1.1.2 s "string value test"

2. Install

2.1 Yum

install snmp
su -c 'yum install net-snmp'

install mib2c
su -c 'yum install net-snmp-perl'

install utils tools:snmpwalk, snmpget,…
su -c 'yum install net-snmp-utils'

3. Steps

3.1 net-snmp中如何加自定义的mib库

  • Write your self-defined mibs
  • Copy to /usr/local/share/snmp/mibs
  • 用snmptranslate -Ts -m ALL 就可以看到所以OID的输出了,当然也包括你新加的。
    如果写的mib文件不正确,应该不能输出,你可以找第三方工具来验证你的文件格式是否正确。
  • 编写相关的函数。
    • 为你的mib新建一个c文件,可以使用mib2c这样的工具来完成。
    • 如果该OID是只读的,那就在该文件里编写一个只读的函数(mib2c应该会生成的);如果可写,也应该有一个写的函数(相样是生成的)

3.2 Self-defined Mib

I don’t find any good tools for generate mibs, what I do is hacking by hand; if
you know some good tool, don’t forget to tell me.

--============================================================
-- Dennis Sample Management Information Base (MIB)
--============================================================

DENNIS-MIB DEFINITIONS ::= BEGIN

IMPORTS
    DisplayString,
    PhysAddress,
    MacAddress,
    DateAndTime,
    RowStatus,
    TEXTUAL-CONVENTION
        FROM SNMPv2-TC
    MODULE-IDENTITY,
    OBJECT-TYPE,
    NOTIFICATION-TYPE,
    Integer32,
    IpAddress,
    TimeTicks,
    Unsigned32
        FROM SNMPv2-SMI;

--============================================================
-- MODULE IDENTITY : dennis
--============================================================

dennis MODULE-IDENTITY
    LAST-UPDATED "201308160000Z"
    ORGANIZATION "MIB"
    CONTACT-INFO
        "Shenzhen, China, Earth village, Solar system
         http://matrix207.github.com
         E-mail: dennis.cpp@gmail.com"
    DESCRIPTION
        "Sample MIB Definition  for
        iso(1).org(3).dod(6).internet(1).private(4).enterprises(1).dennis(9913)"
    ::= { 1  3  6  1  4  1  9913 }


--============================================================
-- Definition of the Sample MIB Common Objects
--============================================================

dennisSampleMib OBJECT IDENTIFIER       ::= { dennis 1 }

--============================================================
-- Definition of the Information Group
--============================================================


dennisSystemInformation OBJECT IDENTIFIER  ::= { dennisSampleMib 1 }

--============================================================
-- Definition of the Information Group (Object type: scalar)
--============================================================

dennisSysInfoEntry OBJECT IDENTIFIER  ::= { dennisSystemInformation 1 }

dennisSystemOSVersion OBJECT-TYPE
    SYNTAX  DisplayString (SIZE (64))
    MAX-ACCESS read-only
    STATUS  current
    DESCRIPTION
        "This object show the operator system version."
    ::= { dennisSysInfoEntry 1 }


dennisSystemOSBit OBJECT-TYPE
    SYNTAX  Integer32
    MAX-ACCESS read-only
    STATUS  current
    DESCRIPTION
        "This object show the operator system bit"
    ::= { dennisSysInfoEntry 2 }


dennisSystemCPU OBJECT-TYPE
    SYNTAX  Integer32
    MAX-ACCESS read-only
    STATUS  current
    DESCRIPTION
        "The speed of processor, unit: MHZ"
    ::= { dennisSysInfoEntry 3 }


dennisSystemMemory OBJECT-TYPE
    SYNTAX  Integer32
    MAX-ACCESS read-only
    STATUS  current
    DESCRIPTION
        "The total number of memory, unit: MB"
    ::= { dennisSysInfoEntry 4 }


dennisSystemDiskSizy OBJECT-TYPE
    SYNTAX     Integer32
    MAX-ACCESS read-only
    STATUS     current
    DESCRIPTION
        "The total size of disks"
    ::= { dennisSysInfoEntry 5 }


--============================================================
-- Definition of the Directory Group Mount on Root (Object type: table)
--============================================================

dennisSubDirOfRootInformationTable    OBJECT-TYPE
    SYNTAX    SEQUENCE OF DennisSubDirOfRootInformationEntry
    MAX-ACCESS    not-accessible
    STATUS    current
    DESCRIPTION
        "A list of expander chassis information."
    ::= { dennisSampleMib 2 }


dennisSubDirOfRootInformationEntry    OBJECT-TYPE
    SYNTAX    DennisSubDirOfRootInformationEntry
    MAX-ACCESS    not-accessible
    STATUS    current
    DESCRIPTION
        "A expander chassis information entry"
    INDEX    { dennisSubDirOfRootIndex }
    ::= { dennisSubDirOfRootInformationTable 1 }


DennisSubDirOfRootInformationEntry    ::=
    SEQUENCE {
       dennisSubDirOfRootIndex Integer32,
       dennisSubDirOfRootName  DisplayString,
       dennisSubDirOfRootSize  Integer32,           
       dennisSubDirOfRootDate  DisplayString }

dennisSubDirOfRootIndex OBJECT-TYPE
    SYNTAX    Integer32
    MAX-ACCESS    read-only
    STATUS    current
    DESCRIPTION
        "The index."
    ::= { dennisSubDirOfRootInformationEntry 1 }

dennisSubDirOfRootName OBJECT-TYPE
    SYNTAX    DisplayString (SIZE (64)) 
    MAX-ACCESS    read-only
    STATUS    current
    DESCRIPTION
        "The type of expander chassis."
    ::= { dennisSubDirOfRootInformationEntry 2 }

dennisSubDirOfRootSize OBJECT-TYPE
    SYNTAX    Integer32
    MAX-ACCESS    read-only
    STATUS    current
    DESCRIPTION
        "The expander chassis manufacturer's id."
    ::= { dennisSubDirOfRootInformationEntry 3 }

dennisSubDirOfRootDate OBJECT-TYPE
    SYNTAX    DisplayString
    MAX-ACCESS    read-only
    STATUS    current
    DESCRIPTION
        "The total number of physical slots for physical
        disk on the chassis."
    ::= { dennisSubDirOfRootInformationEntry 4 }


--============================================================
-- Definition of access object (Object type: scalar)
--============================================================
dennisAccessObject OBJECT IDENTIFIER  ::= { dennisSampleMib 3 }

dennisShutdownType OBJECT-TYPE
    SYNTAX  Integer32
    MAX-ACCESS read-write
    STATUS  current
    DESCRIPTION
        "Use for shudown operator system, 1:shutdown 2:reboot"
        ::= { dennisAccessObject 1 }


--============================================================
-- End Definition
--============================================================
END

3.3 Check mib

[root@localhost mib]# snmptranslate -Tp -m DENNIS-MIB
+--iso(1)
   |
   +--anonymous#0(3)
      |
      +--anonymous#1(6)
         |
         +--anonymous#2(1)
            |
            +--anonymous#3(4)
               |
               +--anonymous#4(1)
                  |
                  +--dennis(9913)
                     |
                     +--dennisSampleMib(1)
                        |
                        +--dennisSystemInformation(1)
                        |  |
                        |  +--dennisSysInfoEntry(1)
                        |     |
                        |     +-- -R-- String    dennisSystemOSVersion(1)
                        |     |        Textual Convention: DisplayString
                        |     |        Size: 64
                        |     +-- -R-- Integer32 dennisSystemOSBit(2)
                        |     +-- -R-- Integer32 dennisSystemCPU(3)
                        |     +-- -R-- Integer32 dennisSystemMemory(4)
                        |     +-- -R-- Integer32 dennisSystemDiskSizy(5)
                        |
                        +--dennisSubDirOfRootInformationTable(2)
                        |  |
                        |  +--dennisSubDirOfRootInformationEntry(1)
                        |     |  Index: dennisSubDirOfRootIndex
                        |     |
                        |     +-- -R-- Integer32 dennisSubDirOfRootIndex(1)
                        |     +-- -R-- String    dennisSubDirOfRootName(2)
                        |     |        Textual Convention: DisplayString
                        |     |        Size: 64
                        |     +-- -R-- Integer32 dennisSubDirOfRootSize(3)
                        |     +-- -R-- String    dennisSubDirOfRootDate(4)
                        |              Textual Convention: DisplayString
                        |              Size: 0..255
                        |
                        +--dennisAccessObject(3)
                           |
                           +-- -RW- Integer32 dennisShutdownType(1)

4. Use mib2c

4.1 mib object

scalar: int, string, time and so on.
table : table has row and column, like table in database.

4.2 use mib2c to create source file

append string “DENNIS-MIB” to config file: snmpd.conf
su -c 'vim /etc/snmp/snmpd.conf'

copy mib file to mibs directory(depend on which directory mib2c installed)
su -c 'cp DENNIS-MIB.txt /usr/local/share/snmp/mibs/'

generate c source file by mib2c
mib2c -c mib2c.scalar.conf DENNIS-MIB::dennis for scalar object
mib2c -c mib2c.old-api.conf DENNIS-MIB::dennis for scalar object
mib2c -c mib2c.iterate.conf DENNIS-MIB::dennis for table object (choise 2)
mib2c -c mib2c.notify.conf DENNIS-MIB::dennis for trapping message
if failed to translate, check the mibs directory again, or look the output message.

more configure files, look as below:

[dennis@localhost net-snmp-5.4.2]$ ls /usr/local/share/snmp/*.conf
/usr/local/share/snmp/mib2c.access_functions.conf    
/usr/local/share/snmp/mib2c.int_watch.conf
/usr/local/share/snmp/mib2c.array-user.conf          
/usr/local/share/snmp/mib2c.iterate_access.conf
/usr/local/share/snmp/mib2c.check_values.conf        
/usr/local/share/snmp/mib2c.iterate.conf
/usr/local/share/snmp/mib2c.check_values_local.conf  
/usr/local/share/snmp/mib2c.mfd.conf
/usr/local/share/snmp/mib2c.column_defines.conf      
/usr/local/share/snmp/mib2c.notify.conf
/usr/local/share/snmp/mib2c.column_enums.conf        
/usr/local/share/snmp/mib2c.old-api.conf
/usr/local/share/snmp/mib2c.column_storage.conf      
/usr/local/share/snmp/mib2c.perl.conf
/usr/local/share/snmp/mib2c.conf                     
/usr/local/share/snmp/mib2c.raw-table.conf
/usr/local/share/snmp/mib2c.container.conf           
/usr/local/share/snmp/mib2c.scalar.conf
/usr/local/share/snmp/mib2c.create-dataset.conf      
/usr/local/share/snmp/mib2c.table_data.conf
/usr/local/share/snmp/mib2c.genhtml.conf             
/usr/local/share/snmp/snmptrapd.conf

5. Add self-defined mib library

5.1 steps:

使用net-snmp扩展私有MIB的大致方法
参考: net-snmp tutorial

step 1. 首先需要使用net-snmp的相关API编写MIB相关C代码,

1. MFD/mib2c:这是一种通过net-snmp提供的mib2c程序自动生成相关代码的方式
2. A simple scalar attached to a variable:适合于简单变量类型的object
3. A simple scalar with the value returned from code:适用于任何变量类型的object

step 2. 然后将刚写的MIB C code编译进net-snmp,有几种方法:

1. compile it into master agent:
  1)将刚编写的码加入net-snmp的src目录,
  2)通过configure的option指示make编译该mib,
     如./configure --with-mib-modules="myobject" 
  3)make
  4)make install
  这样,你的MIB就已经被内置如snmp服务程序中了,MIB的生效也就理所当然

2. compile your code into a “subagent”:
  这种方式可以将subagent通过agentx协议与master agent通信,
  参考: http://net-snmp.sourceforge.net/tutorial/tutorial-5/toolkit/demon/index.html
  这种情况subagent最终是一个独立的application,包含两种生成方式,
  1).是通过net-snmp-config工具生成;
  2).是自己编写程序控制调用;
  后者更为灵活,subagent功能可以被集成在其它application中。

3. compile your code into pluggable shared object and tell the snmpd agent to load it
  这种方式最后生成一个.so的共享库,用户启动snmpd服务时可以通过指定参数的方式
  加载该共享库以扩展MIB,
  参考: http://net-snmp.sourceforge.net/tutorial/tutorial-5/toolkit/dlmod/index.html 

5.2 compile net-snmp by shell script

cd /home/dennis/net-snmp-5.4.2/
cp -f /home/dennis/dennis.{h,c} ./agent/mibgroup
./configure --prefix=/usr --with-mib-modules="dennis"
make clean
make
make install

5.3 update mib

copy "net-snmp-5.4.2/agent/.libs/libnetsnmpmibs.so" to "/usr/lib/",  
64bit system is to "/usr/lib64/"

5.4 check the so had have the interfaces which you implement

[root@ ~]# which snmpd
/usr/sbin/snmpd
[root@ ~]# ldd /usr/sbin/snmpd | grep libnetsnmpmib
    libnetsnmpmibs.so.15 => /usr/lib64/libnetsnmpmibs.so.15 (0x0000003655600000)
[root@ ~]# nm /usr/lib64/libnetsnmpmibs.so.15 | grep "YOUR_INTERFACE_KEY"

5.5 check config file

check the file /etc/snmp/snmpd.conf, append `rwcommunity private` to it to 
make access authority for `snmpset`.

5.6 restart snmp

/usr/sbin/snmpd -c /etc/snmp/snmpd.conf -Le

6. Improve speed

When snmp agent receive lots of request, and if do some I/O operation of each
request, that will show a low performance.
One not-bad solution is use a thread do the I/O job, and store the information
to memory (using global variable, array, structure …), then fill info and
return to client when get the request.

6.1 add thread function to mib file

Add include #include <pthread.h> to the mib file
Write thread function like this: void *update_all_info (void *parm);

6.2 modify Makefile

Add link library(-lpthread) to agent/Makefile (line:199):
LOCAL_LIBS = -L../snmplib/.libs -L../snmplib -L./.libs -L./helpers/.libs -L./helpers -lpthread

7. Reference

get mac with NetBIOS

Find port 139 open machine:

[root@localhost netbios]# nmap -p139 192.168.50.22

Starting Nmap 6.25 ( http://nmap.org ) at 2013-04-10 20:49 CST
Nmap scan report for 192.168.50.22
Host is up (0.00023s latency).
PORT    STATE SERVICE
139/tcp open  netbios-ssn
MAC Address: 00:**:09:**:3a:** (Dell)

Nmap done: 1 IP address (1 host up) scanned in 0.12 seconds

Search all:

Linux shell command : 
    nmap -p139 192.168.50.1/24 | sed '/./{H;$!d};x;/open/!d' | grep 'scan' | awk '{print $NF}'
or
    nmap -p139 192.168.50.1/24 | grep -B 3 'open' | grep 'scan' | awk '{print $NF}'
or 
    nmap -p139 192.168.50.1/24 | tac | sed -n '/open/,+3p' | tac | grep 'scan' | awk '{print $NF}'    

[root@localhost netbios]# time nmap -p139 192.168.50.1/24 | grep -B 3 'open' | grep 'scan' | awk '{print $NF}'
192.168.50.16
192.168.50.22
192.168.50.26
192.168.50.28
192.168.50.29
192.168.50.36
192.168.50.37
192.168.50.39
...
192.168.50.238

real    0m4.451s
user    0m0.067s
sys    0m0.024s

scan all:

[root@localhost netbios]# nmap -p139 192.168.50.1/24 | grep -B 3 'open' | grep 'scan' | awk '{print $NF}' | xargs -n1 ./netbios
 192.168.50.16 : 00-**-19-**-46-**
 192.168.50.22 : 00-**-09-**-3a-**
 192.168.50.26 : b8-**-6f-**-06-**
 192.168.50.28 : 00-**-ae-**-e6-**
 192.168.50.29 : 00-**-c9-**-6d-**
 192.168.50.35 : 00-**-19-**-59-**
 192.168.50.36 : 00-**-ae-**-ee-**
 192.168.50.37 : 00-**-c9-**-8c-**
 192.168.50.39 : 00-**-9b-**-b9-**
...
192.168.50.238 : b8-**-6f-**-28-**

netbios.c source code:

// netbios.c
// Get mac address with NetBIOS protocol
// compile with gcc on linux: gcc -o netbios netbios.c
// Create by Dennis
// 2013-04-10
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>

#define NETBIOS_PORT 137

unsigned char bs[50]={ 
    0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x43,0x4b,0x41,0x41, 
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 
    0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x0,0x0, 
    0x21,0x0,0x1
};

// 通过NbtStat获取计算机名字信息的结构体  
struct names  
{  
    unsigned char nb_name[16];  // 表示接收到的名字  
    unsigned short name_flags;  // 标识名字的含义  
}; 

void print_buf(unsigned char* buf, int len)
{
    int i = 0;
    for (i=0; i<len; i++)
    {
        if (i%8==0) printf("\n%04d: ", i);
        printf("0x%02x ", buf[i]);
    }
    printf("\n");
}

void parse_netbios(char* buf, int len)
{
    //print_buf(buf, len);
    char* respuesta = buf;

    // 定义名字数组  
    struct names Names[20*sizeof(struct names)]; 

    int count = 0;

    // 将count定位到名字表中名字的数量。在NetBIOS回应包中,前面56位是网络适配器的状态信息  
    memcpy(&count, respuesta+56, 1);  
    if(count > 20){      // 最多有20个名字,超出则错误  
        return;  
    }  

    // 将名字表内在复制到Names数组中  
    memcpy(Names,(respuesta+57), count*sizeof(struct names));  

    // 将空格字符替换成空  
    unsigned int i,j;
    for(i = 0; i < count;i++) {  
        for(j = 0;j < 15;j++){  
            if(Names[i].nb_name[j] == 0x20)  
                Names[i].nb_name[j]=0;  
        }  
    }

#if 0
    for(i=0;i<count;i++){  
        // 如果最后一位是0x00,则表示当前名字表项为保存计算机名或者工作组  
        if(Names[i].nb_name[15] == 0x00){  
            char buffers[17] = {0};  
            memcpy(buffers, Names[i].nb_name, 16);  
            // 使用name_flags字段来区分当前名字是计算机名还是工作组  
            if((Names[i].name_flags & 128) == 0) {  
                printf(" name: %s\n", buffers);
            }  
            else{  
                printf("group: %s\n", buffers);
            }  
        }  
    }  
#endif

    unsigned char mac[6];
    // 名字表后面是MAC地址  
    memcpy(mac,(respuesta+57+count*sizeof(struct names)),6);  
    printf(": %02x-%02x-%02x-%02x-%02x-%02x\n", 
        mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
}

int main(int argc,char*argv[]) {
    int ret=-1;
    int sock=-1;
    //int so_broadcast=1;
    struct sockaddr_in server_addr;
    struct sockaddr_in from_addr;
    int from_len=sizeof(from_addr);
    int count=-1;
    fd_set readfd;

    struct timeval timeout;
    timeout.tv_sec=2;
    timeout.tv_usec=0;

    sock=socket(AF_INET,SOCK_DGRAM,0);
    if (sock<0) {
        printf("HandleIPFound:sock init error\n");
        return 1;
    }

    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(NETBIOS_PORT);
    //server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (argc > 1)
        server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else
        server_addr.sin_addr.s_addr = inet_addr("192.168.50.39");

    //ret = setsockopt(sock,SOL_SOCKET,SO_BROADCAST,&so_broadcast,sizeof(so_broadcast));

    char recvbuf[1024] = {0};
    int times=10;
    int i=0;
    for (i=0; i<times; i++) {
        timeout.tv_sec=2;
        timeout.tv_usec=0;
        //printf("==> IP :%s Port:%d\n",inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
        ret = sendto(sock,&bs,sizeof(bs),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
        if (ret==-1) {
            printf("Failed to do sendto\n");
            continue;
        }

        FD_ZERO(&readfd);
        FD_SET(sock,&readfd);

        ret = select(sock+1,&readfd,NULL,NULL,&timeout);
        switch (ret) {
            case -1:
                break;
            case 0:
                printf("%s timeout\n", inet_ntoa(server_addr.sin_addr));
                return 0;
                break;
            default:
                if(FD_ISSET(sock,&readfd)) {
                    memset(recvbuf, 0, sizeof(recvbuf));
                    count=recvfrom(sock,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&from_addr,&from_len);
                    printf("%16s ", inet_ntoa(from_addr.sin_addr));
                    parse_netbios(recvbuf, count);
                    return 1;  
                }
                break;
        }
    }

    return 0;
}

Reference: