1. Trang chủ
  2. » Công Nghệ Thông Tin

Tài liệu PERFORMANCE MANAGEMENT OPPORTUNITIES doc

72 479 2
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Performance Management Opportunities
Tác giả Zeev Suraski, Brad Young
Chuyên ngành Performance Management
Thể loại Article
Năm xuất bản 2002
Định dạng
Số trang 72
Dung lượng 1,39 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

In the case ofListing 2, however, I have used a slightly more complexversion of it called ncurses_mvwaddstr, which atthe same time moves the cursor to a pre-defined posi-tion and prints

Trang 1

The Magazine For PHP Professionals

Reviewed for you:

Zend Performance Suite

Davor's PHP Editor

Plus:

SNMP Management With PHP

PHP nCurses Shell Programming

Writing a Webmail System

Distill PDFs For Free With PHP

Create a DB Abstraction Layer

Trang 2

Technologies Ltd.

for evaluation version and ROI calculator

Zend Performance Suite

Reliable Performance Management for PHP

Serve More.

With Less.

Trang 3

41 | Creating A Visual Catalogue

System with PHP and IE

Trang 4

Marco Tabini

Editors

Arbi Arzoumani Marco Tabini

Graphics & Layout

Young

php|architect (ISSN 1705-1142) is published twelve times a year by Marco Tabini & Associates, Inc., P.O Box 3342, Markham, ON L3R 6G6, Canada

Although all possible care has been placed in assuring the accuracy of the contents of this mag- azine, including all associated source code, listings and figures, the publisher assumes no responsibil- ities with regards of use of the information con- tained herein or in all associated material.

Contact Information:

General mailbox: info@phparch.com Editorial: editors@phparch.com Subscriptions: subs@phparch.com Sales & advertising: sales@phparch.com Technical support: support@phparch.com

Copyright © 2002 Marco Tabini & Associates, Inc — All Rights Reserved

Here we are After months of

preparations—and a few

sleepless nights—you hold in

your hands the very first copy of

php|architect!

While most editors will spend

their first editorial explaining at

length the arduous path the

brought the fruit of their labor to

life, I feel I have a higher goal to

aspire to Getting php|a off the

ground has introduced us to

some genuinely new

experi-ences—nothing beats being

awake at two o’clock in the

morning (when you couldn’t

read the lettering off a truck if it

hit you) trying to pick a highly

legible font that will work both

on the screen and in print Still, I

don’t think you want to hear

about that

Let me, instead, give you an

idea of how this issue is

organ-ized, so that you can take

advan-tage of it to its fullest potential

We chose PDF and electronic

distribution as a move that, we

thought, would drive down our

costs and, in turn, make it

affordable for anybody in the

world to subscribe or buy a

sin-gle issue of php|a Still, we

rec-ognize that there are some

unique challenges related to

using this distribution strategy,

and we have taken a few steps

that (we hope) will make it

easi-er for eveasi-eryone to enjoy our

magazine

The PDF file that you have

received has been designed

both for on-screen viewing and

for printing on a regular inkjet or

laser printer This way, you can

look the contents over directly

on your computer, and you can

print whatever interests you—or

the whole thing, if you like the

feel of paper in your hands In

fact, although the PDF file itself

is encrypted and you should notredistribute it electronically, youcan print as many copies of it asyou like

If you decide that on-screenviewing is your bag, then hereare a couple of pointers Most ofour articles are hot-linked to theInternet—whether it’s an e-mailaddress to contact an author orthe URL of a website from whichyou can download some greatinformation Links are an oppor-tunity for us to offer you some-thing that a print magazine willnever be able to—a direct con-nection to the world Naturally,all our advertisements are alsolinked—so, show us your sup-port and do visit our advertisers’

websites They all offer greatproducts No, really, they do!

To make the navigation easier,you will find a full table of con-tents, which can be accessedfrom within Acrobat’s book-marks tab

Notwithstanding all thesesteps we’ve undertaken, PDFremains a “new” way of publish-ing a magazine, and we can onlyhope to get better with time

Your feedback in this process iscrucial—we want to hear whatwe’ve done right, but, especial-

ly, we want to hear what we’vedone wrong, so that we can fix

it Drop us an e-mail atinfo@phparch.com and let usknow what you like and don’tlike, what topics you want to seediscussed in future issues andany comments you may haveabout this month’s articles

Happy reading!

Trang 5

Open-source Gets a Rap-around

As open-source software becomes more and more

accepted as a viable alternative to its commercial

coun-terpart in IT industry, it’s becoming painfully obvious

that it’s necessary to build an infrastructure to facilitate

the exchange of information between developers and

customers

Thanks to its open

nature, OSS lends itself

well to customization—

something that is

nor-mally unthinkable with

commercial products

Since there is no

licens-ing cost, companies find

it easy to use

open-source projects as the

basis for larger initiatives

that have very specific

requirements This

results in ad-hoc

solu-tions that are based on

solid, well-tested code

and can be deployed in

a short time

Open-source developers are in the unique position of

possessing the knowledge needed to successfully

mar-ket customization and special features to their software

packages Unfortunately, they often suffer from a lack

of exposure that would allow them to defray

develop-ment costs of new features by offering such features to

multiple customers at the same time

Marcheka Software Ltd aims at filling the void

between OSS vendors and clients through their Rap-X

offering, which can be found at http://www.rap-x.com

Labeled as a “collaborative sales & support platform”,

Rap-X works by allowing prospective clients to make

requests for new features to be added to software

pack-ages in a collaborative way, so that multiple requests for

similar changes can be merged into a single one and

the development costs can be amortized among

sever-al players

With this approach, everyone ends up a winner:

developers get the exposure they need and can open

new markets for their products while still benefiting thepublic at large by keeping their products free.Customers, on the other hand, benefit from the factthat they can enjoy customized software at a fraction ofthe costs normally associated with it

Rap-x, which already counts five different source projects among its clients, is offered both as anASP (as in Application Solutions Provider, not ActiveServer Pages!) product and through a more traditionallicensing model You can find its website here

open-PHP 4.3 Inches NearerWith the release of RC 2 at the end of November , thenew PHP version 4.3 is one step from its final release.The new version of our beloved language includes sev-eral enhancements and performance optimizationscompared to its predecessor Our favorite one is theability to fetch SSL-protected pages directly throughthe fopen() URL wrappers—a major step forward, inour opinion, toward making PHP ever more appealingfor commercial applications

You can download PHP 4.3 RC2 from the PHPGroup’s main page

iConductor costs $1,295.00 and is available directlyfrom Farpointer at http://www.i-conductor.com

You’ve got a great software

product and committed users,

but you need additional

fund-ing to develop the features

they require.

php|a

Trang 6

For a vim addict (recently converted to using gvim

and the open-source CREAM extension) like myself,

GUI IDEs are something to look at with an air of

suspi-cious resentment On one hand, they often look easy to

use and user-friendly (something that can’t really be

said about vim) On the other, however, my shell junkie

friends and I fear that, as soon as you need to do

some-thing out of the ordinary, they will turn into rigid

mon-sters that won’t allow you to move past their pre-set

functionality

Luckily, I’ve had a chance to find out that it’s not

nec-essarily always the case After having been scarred for

years by Microsoft’s Visual Studio IDE, I had a chance to

use Zend Studio, which I found extremely powerful

(particularly in its debug functionality, which are very

valuable), although made a little slow by the fact that

it’s based on Java Still, even for its price, Zend Studio is

extremely good—and works across multiple platforms

A few weeks ago, I came across Davor Pleskina’s PHP

Editor (DPHPE from now on), which is available from

his website at http://www.pleskina.com Davor is a

great guy (he’s also the author of this month’s article on

catalogue management), and he suggested that I give

his IDE a try

Firing UpDPHPE is extremely easy to install, although it does-

n’t come with a setup program of its own You do have

to manually copy the files from the ZIP file you can find

at Davor’s website into a folder on your PC and createthe necessary shortcuts I can’t see this as being a hugeproblem, since it’s safe to assume that you probablyhave some level of confidence with the OS if you’reinterested in this product Still, creating a setup pro-gram is not a particularly challenging endeavor thesedays—there are plenty of tools for that—and a moreguided approach to installation would probably benefitmost users To give you an example, during my firstattempt at running DPHPE, I simply executed the appli-cation’s main executable from directly within WinRAR(my compression application) without copying anysupporting files The results were not catastrophic—theapplication was running fine for the most part—but Ihad lost access to some functionality, such as keywordautocompletion

Once installed, DPHPE takes up very little space on

Reviewed For You

Davor’s P PHP Editor

By Arbi Arzoumani

DPHPE is also able to tain a list of all the variables that are currently in scope in your script

Trang 7

main-your hard drive, which luckily also means that it uses

very little RAM when it has to run As much as this

does-n’t seem to be a big problem these days, if your

machine is running a number of different services at the

same time, the last thing you need is another

applica-tion that gobbles memory

Basic FeaturesWhere do I begin? DPHPE has a wide array of differ-

ent features that are undoubtedly helpful for any

devel-oper Most impressive of all, these features are

imple-mented in a simple and efficient way, so that they

actu-ally help without getting in the way (at least most of

the time) The editor that comes with the application

features syntax highlighting (for HTML, PHP, Javascript

and CSS), and its aspect is fully customizable—both

from the point of view of the fonts used and of thecolours in which the syntax is rendered Also featured inthe editor is the ability for auto-completion of key-words, which is particularly useful for a language ascomplex as PHP It is also possible to have the applica-tion create a list of functions defined in your scripts,although this doesn’t always work well—for example, Ihad a comment that contained the word “function”,and the editor mistakenly recognized it as a declara-tion

DPHPE is also able to maintain a list of all the variablesthat are currently in scope in your script (although,unfortunately, it is unable to extend this capability toany include files you may be using) and to automatical-

ly pop up a window that allows you to choose a able whenever you type the dollar sign This featuretends to be a bit annoying after a while, but it can beturned off and the variable list can be used manually asneeded

vari-My only real beef with the editor is that I couldn’tfind a way to word-wrap my long lines of code Noteveryone uses this feature, but I find it very convenientand not having it makes it a bit difficult to use the edi-tor in a comfortable way

Another well-developed

fea-ture of DPHPE is the ability to

create and maintain projects.

Trang 8

Project Management and Web PreviewAnother well-developed feature of DPHPE is the abil-

ity to create and maintain projects A project is simply

a repository of files but, contrary to a folder, it also

pos-sesses a number of properties that make it possible, for

example, to preview entire web projects at

the click of a mouse What’s most

interest-ing, however, is that the concept of projects

lays the foundation for a number of future

expansions, such as encoding, CVS or

SourceSafe compatibility, global search and

replace, and so on, that could all be

accessi-ble at the click of a mouse

Speaking of web preview, if you have

Internet Explorer installed on your machine

(which you must have, unless you’re

run-ning some ancient version of Windows),

DPHPE is capable of interfacing to it and

visualizing your PHP scripts by executing

them through your web server If you prefer,

you can also launch IE separately rather than

within the editor

Unfortunately, DPHPE does not feature an

internal debugger—and I personally think

that this should be a very important future ment Still, this application offers a great deal of func-tionality at a very convenient price—it’s free What’smore, there is no big corporate presence behind it, sothat it really evolves following the needs of its users,and Davor does a great job of listening

enhance-php|a

Trang 9

To start, let’s consider the reasons why we should be

concerned with Performance Management In a

nutshell, it can be summarized with two simple goals:

1) Save Money

2) Happy Customers

Although this may seem obvious, or even trite, it is

important to keep in focus when evaluating and

deploying performance-related products

To take it one level deeper, let’s understand the issues

faced by IT managers and webmasters today:

· High expenses for hardware and software

pur-chases to support http site traffic

· High expenses for database server hardware

and software

· IT administration time and money spent to

reconfigure and maintain server farms

· Low customer satisfaction resulting from

unrea-sonable latency time and load time

With these issues in mind, we can now more clearly

attack the question of how to achieve improvements

for our projects

Options For Performance

ManagementThere are many different ways through which youcan squeeze more juice from your limited hardwareserver resources These options include hardware, soft-ware, and networking alternatives We’ll highlight each

of these options below Note that these alternatives arenot mutually exclusive

Hardware Options

More Hardware - The most common indicator –

aside from emails from angry site users – that raises ared flag about performance issues is maxed out CPUutilization And when this happens, an obvious butcostly solution is simply to buy more hardware If youhave two dual-CPU http server machines whose CPU’s

Performance Management Opportunities

By Zeev Suraski and Brad Young

Why Focus on Performance Management? A look at the various alternatives that exist in today’s complex

IT world to improve server performance for sites that run PHP

As Yogi Berra famously stated,

“It’s hard to make predictions, especially about the future.” For PHP, however, there are some things that are not so difficult to predict

Trang 10

are maxed out, then adding a third machine, with load

balancing, will alleviate some of this load stress It is

worth noting that this solution works for steady state

performance issues, but does not scale well for spiked

performance issues For example, if your site

experi-ences dramatic increases in requests at specific times of

the day, you’ll need to add enough hardware to cover

the ‘worst-case’ load (Actually, it is more accurate to

call this the ‘best-case’ We want more traffic on our

sites, after all.)

Smarter Hardware – Similar to adding more

hard-ware, adding smarter hardware can provide

perform-ance improvements This includes the more obvious

steps of having machines with faster CPUs, or more

CPUs per machines Beyond that, there is also hardware

components such as disk drives with built-in memory

caching that also help improve hardware performance

Hardware Devices – Another hardware option to

improve performance is to utilize hardware devices that

have built-in algorithm circuitry For example,

perform-ing SSL encryption usperform-ing software on your http server

adds a significant stress to the CPU load You can add a

hardware card that performs this encryption without

requiring any CPU time, thus lowering the effective

load Similarly, this can be done for gzip compression as

well

Faster Code

It is clear that if your code is faster and more efficient,

you’ll be able to handle more requests with the same

hardware Let’s explore how we can make code run

faster

C Libraries– The massive adoption of PHP has come

about mainly due to its ease of adoption – both from a

cost perspective as well as a development perspective

But while PHP speed has improved tremendously, it is a

fact that C code runs faster So if you develop some of

your more algorithmically-complex functions in C and

call out to them from within your PHP scripts, you can

speed up your code Of course, by doing this, you give

up some of the flexibility, transportability and ease of

use that brought you to PHP in the first place

Write Better Code – Better coding practices, while

tiresome at times to adopt, pay off in the long run

Always stay up to date and aware of the best ways to

use PHP by following email lists from php.net and

read-ing tutorial materials at sites like PHPbuilder.com and at

the Zend Developer Zone (devzone.zend.com)—not to

mention the pages of php|a, of course!

Code Acceleration – PHP is an interpreted scripting

language This means that each time a PHP script

needs to run, the core Zend Engine will parse that file

and compile it in memory, then execute it Since the

PHP scripts themselves typically don’t change from

request to request, this parsing and compilation phase

is repetitive and wasteful Code Acceleration productsavailable today can eliminate that redundancy by main-taining the compiled version of the file in memory

Output Content CachingImagine that your friend asked you what the headline

of the New York Times was today To respond, you gooutside, walk two blocks to the nearest news-stand,read the headline of the New York Times, then comeback and tell him what that headline says (We leave it

to you to understand why your friend would ask this,but just go with the analogy)

Now, imagine that another friend calls you and asksthe exact same question Would you repeat the journeydown to the news-stand? Unless you are madly in lovewith the salesperson, probably not You are smartenough to identify that this new request is identical tothe previous request, and hopefully you are also smartenough to remember what the headline was

Well, unfortunately your http server is not as smart asyou, and it, figuratively speaking, is probably makingway too many trips down to the news-stand (read:database server) This is where content caching comes

in The idea of content caching is straightforward: yourserver works hard to generate some results to a specif-

ic request Then it keeps those results on hand, and if itgets a similar request, it re-uses the previously generat-

ed results It is interesting to note that caching takesplace at the http server level and reduces load on thatserver, but an even bigger load savings is experienced

on the database server, which sees dramatically lessSQL requests coming in

The concept is simple enough, but with all thingsprogrammatic, the devil is in the details The challengemainly boils down to:

· Cache Hit Identification – How does the cachedetermine if this is indeed a similar request, or

is it a different request (i.e Is my second friendasking for the same headline, or is he asking

PHP is an interpreted ing language This means that each time a PHP script needs to run, the core Zend Engine will parse that file and compile it in memory, then execute it.

Trang 11

script-about the New York Post, or is he asking script-about

yesterday’s New York Times, or is the headline

different for him because he gets the national

edition instead of the local edition)?

· Cache Management – How does the cache

manage the information results that are

on-hand, purge old information, and prevent

over-use of system resources (i.e I can’t

remember every headline of every newspaper

from every day without my head exploding, so

which is the best to forget)?

Now let’s look at a few ways of achieving contentcaching:

File-level Content Caching – If you have web pages

that appear the same to many users, you can store theentire results of the URL request coming in to your httpserver, as shown in Figure 1

For example, imagine a page that lists news lines, such as cnn.com New headlines are added, per-haps at a rate of one new headline per 15 minutes.CNN could cache the headlines page or at least 5 min-utes Now, if 1,000 page view requests come in duringFigure 2 - Partial Page Caching

head-Figure 1- Content Caching Schematic

Trang 12

that 5 minute period, only one actual PHP execution

and one database access will occur, a dramatic

decrease compared to the 1,000 that would otherwise

occur All the other requests are served directly from

the cache, as if it was a static html file And by having

a time limit to this cached file, the worse case scenario

for content freshness would be that it would take a new

headline 4 minutes and 59 seconds to make it to the

site

When exploring solutions, you’ll want to make sure

that there is sufficient flexibility for determining what

should be considered a ‘cache hit’ Besides the basic

URL address, you might also take specific cookie

vari-ables, GET parameters, or global http variables into

consideration For example, you might want to cache a

different version of your home page for each

geograph-ic region, or type of user

Partial PageCaching – Solving the “Welcome,

UserName” Problem– The benefits of file-level content

caching are clear But often, some portions of the page

are personalized on a per-user basis Let’s call this the

“Welcome, UserName” problem In our headline ing page, the main portion of the page is the same foreach user, but now we add a personalization message

list-at the top of the page We can no longer cache theentire file, unless we don’t mind that users named Judy,John, and Jade will get a page that says “Welcome,Joe”, the first person to request the URL

The solution here is partial page caching We still cancache the majority of the page, which in this case alsouses the most resource-intensive database queries This

is done at an API level, in which the caching of datasegments is maintained and reused programmatically,

as shown in figure 2

Database Intermediary Caching– A third option for

caching of results involves separating your PHP codefrom the database, as shown in Figure 3 Instead, have

a separate timed job that pulls content from the base and creates chunks of html files that the PHPscripts then can read in the output results that they arecreating

data-This solution adds some complexity with regards tosystem configuration and code dispersal, but the resultswill sometimes make it worthwhile

Figure 3 - Database Intermediary

Publish your data fast with PHPLens

PHPLens is the fastest rapid application tool you can find for publishing your databases and creating sophisticated web applications Here’s what a satisfied customer, Ajit Dixit of Shreya Life Sciences Private Ltd has to say:

I have written more than 650 programs and have almost covered 70% of MIS, Collaboration, Project Management, Workflow based system just in two months This was only possible due to PHPLens You can develop high

quality programs at the speed of thinking with PHPLens

Visitphplens.comfor more details Free download

PHP 5.0, with its new Zend Engine 2, will include new object handler capabilities that will not only make coding easier, it will also speed up application performance.

Trang 13

Database Optimization

Data Models and SQL Tuning – As programmers,

we often find ourselves focusing on our area of

expert-ise – coding However, even the fastest optimized

algo-rithm as written in PHP can’t make up for the time

wasted by poorly written SQL statements or queries to

databases that are not modeled or indexed properly

Don’t fall into this trap As mentioned earlier under

the subject heading of “Write Better Code”, take the

time to learn about how to get the most out of your

database server If you have a DBA in house, use his or

her expertise As with content caching, a little effort can

go a long way when you tweak your database usage

Data Compression

Gzip Compression – “Performance problems” is a

big umbrella Sometimes, the performance issue is not

necessarily server throughput or CPU utilization, but

rather end-user experience of download time Slow

download may result from either large data transfer, or

overutilization of server bandwidth In both cases, you

will improve end-user performance experience by

com-pressing your data

Most browsers today can receive compressed

con-tents, and will decompress automatically Using output

compression is as simple as setting

zlib.output_com-pression = on in php.ini Browser detection will

deter-mine if gzip support is built in, or if the original plain

text should be sent

However, it is important to note that this adds some

additional CPU burden to your server, which must

per-form a gzip compression for each result being

deliv-ered This can be overcome by using a compression

solution that is integrated with content caching In this

case, the compressed version is cached, and not

regen-erated each time

Network InfrastructureWhile it isn’t directly related to PHP dynamic content,

it is worth noting that network optimization solutions,

such as those provided by Akamai , can boost content

delivery time This can be especially useful if image

download and delivery time is your performance

bot-tleneck

Which Should You Choose?

As stated in the opening of this article, these options

are not necessarily mutually exclusive So, how do you

make choices on how to boost your performance?

Well, in the words of the anthropologist Margaret

Mead, “Always remember that you are absolutely

unique, just like everyone else.”

First, identify what specific problems you are having

Also, identify the resources and infrastructure you haveavailable Once you measure these parameters, select-ing the various products or methodologies mentionedabove becomes much easier

Enterprise Performance – Future

es the necessary level We may see some boosts

to performance from Apache 2.0’s threaded capabilities, on platforms such asSolaris

multi-· PHP 5.0, with its new Zend Engine 2, willinclude new object handler capabilities that willnot only make coding easier, it will also speed

up application performance

· In the database world, newer extensions andsupport for MySQL 4 and Oracle will likely giveanother shot in the arm for database through-put

Why Performance Management

Matters

We opened this article explaining the benefits thatPerformance Management brings you and your organ-ization: Save Money and Happy Customers

But the importance runs even deeper Collectively,the PHP community is proving every day that PHP isready for front-line, enterprise-level applications.Implementing performance management solutionssuch as those highlighted here bring us further towardsthe recognition from the marketplace that PHP is here

es Brad Young leads the Product Management team at Zend, and has over 15 years experience in on-line database and XML systems deployment You can reach both Zeev and Brad via Zend's website at www.zend.com.

php|a

Trang 14

In a world of web-based management systems and

graphical user interfaces, it’s comforting to find out

that shell programming is still regarded as an important

piece of the IT puzzle Don’t get me wrong—I’m not

saying this merely out of nostalgia for the times when I

had to make do with 48kB of RAM for operating

sys-tem, program and data, or had to wrestle a few cycles

of program execution out of a mainframe Text-based

applications are still very useful in a number of

situa-tions, particularly when simplicity is called for or a GUI

would be oppressively slow to deal with (such as when

you’re trying to manage a web server that is heavily

loaded and cannot spare much bandwidth for you)

Shells, particularly (but not exclusively) in the UNIX

world, are very much alive and kicking, and have

advanced significantly in complexity and functionality

over the past few years

nCurses - A Library For

Cool Shell AppsThink “shell application” and the image of a dull,

black-and-white text screen comes to mind Luckily,

most terminals are actually capable of a lot more than

just displaying text: they provide a variety of colours

and effects (such as underlined or bold—”bright”—

text), mouse compatibility and much more It’s

normal-ly rather difficult, however, to take advantage of these

capabilities for a number of reasons First, not all

termi-nals are created equal—there are several different

stan-dards available, each with its own commands andcapabilities Second, because terminal systems have towork over simple serial communication lines, the com-mands they support are often intricate combinations ofASCII characters Since shell applications are usuallywritten as a quick and easy solutions to simple prob-lems, the developers rarely have the time and patience

to bother with adding the “frills” to their interfaces

Those of you who, like me, have spent their youthwasting their allowances on telephone bills as a result

of the expensive hobby known as “BBS”, however,know what terminals are capable of Remember thecool menus, the dazzling animations and the colourfulinterface? Compare that with your average shell appli-cation and, well, I’d take the BBS over it every day

On UNIX systems, the capabilities of terminals arestored in the termcap database, a file usually found inthe /etc directory that is used by applications such asthe original vi editor Termcap contains a description ofeach terminal that is recognized by the system (forexample, “dumb” or “ansi-compatible”), together withthe commands it supports

Termcap, however, is not in a very ly” format In fact, I think it’s fair to say that using it in

ter is rather unbeatable when you have to process large data files).

PHP Version: Latest PHP CVS Build O/S: nCurses-compatible O/S Web server: N/A

Support Tools: nCurses Library REQUIREMENTS

Trang 15

an application with no outside help would require a

major effort, since you would only have to interpret its

contents, but also write the necessary functions to

han-dle tasks like cursor positioning, text attributes, window

management, and so on

It was probably out of desperation that, around

1980, a student at UC Berkley named Ken Arnold

devel-oped a series of routines that provided an essential

framework for terminal interface development and

col-lected them in what became known as the “curses”

library Curses then became the basis for Rogue,

possi-bly one of the first-ever Role Playing Games, initially

developed for BSD Unix but eventually ported to

pret-ty much any operating system ever conceived If you

have never played Rogue, I suggest you give it a try—

not only it’s a thoroughly enjoyable and never

repeti-tive game, but it also gives you an idea of how

engag-ing a simple text-based user interface can be You can

find Rogue in RPM format at :

http://www.rpmfind.net/linux/rpm2html/

search.php?query=rogue

I should warn you that you will find it highly

addic-tive; your productivity will probably drop to zero for a

while, but you’ll have lots of fun in the bargain

The Curses library was picked up by Bell Labs, whichincluded it in its System III version of UNIX, introducingseveral changes to termcap in the process Termcap’snew format, called terminfo, offered more functionali-

ty Additional development was done through the1980s, and in 1993 the package was finally renamedncurses

Ncurses and PHPThe ncurses library provides a number of features,such as:

· Formatted input and output on a variety of minals

ter-· Support for colours and various text styles, such

as underline, bold, and so on

· Support for windowing

· Support for mouse inputPHP support for the ncurses library is still very much

in its infancy, and is based on the GNU implementation

of the library, available via FTP at:

ftp://ftp.gnu.org/pub/gnu/ncurses

Proven Open Source Expertise

• When you need to deploy a web-serving

framework capable of delivering millions of views

and transactions per day

• When you need to construct complex document

and task workflows that match your organizations'

needs

• When you need to create a world wide streaming

network capable of 99.99% up time

• When you need to protect your country's

sovereignty by avoiding "random" changes in

software licensing that can affect the price of

each transaction your citizens make

• When you need the level of security that only

expertise and transparency can provide you

• When you need Single-Sign-On across multiple vendors serving a diverse constituency

• When you need to reduce your reliance on proprietary software solutions and realize the promise of your existing investments

The Tribal Knowledge Group's demonstrated credentials in the Open Source Software community enable them to provide you with decades of software engineering, architecture, security and strategy expertise via an

unprecedented think-tank of Internet technology and open-source experts.

Contact us at info@tribalknowledgegroup.com to learn how the Tribal Knowledge Group can help maximize your existing investment for

the future.

Trang 16

It was originally introduced by Hartmut Holzgraefe

and Georg Richter with version 4.1.0 of PHP and is still

considered highly experimental, to the point that the

official PHP documentation covers the ncurses

func-tions only very quickly and in a still incomplete fashion

Luckily, the ncurses extension functions follow their

original counterparts very closely, which actually makes

the process of creating an ncurses-powered PHP script

very easy (this is something that the authors of the

extension must have undoubtedly known, so that they

have—correctly, in my opinion—put all their efforts in

building out the extension)

Ncurses is actually supported on a number of

plat-forms, including all the UNIX derivatives, cygwin and

BeOS

Obtaining the RightVersion of PHPThe problem with making ncurses run on your server

is that, in order to gain access to enough functionality,

you will have to download the most recent version of

PHP from the public CVS tree There is no way around

this problem, as the stable releases available to this

point do not yet include the latest code, which is

unfor-tunately necessary to perform most of the basic

ncurs-es tasks

Luckily, obtaining the latest version of PHP is not

dif-ficult at all You can get it directly by issuing a couple of

CVS commands, which are clearly explained in the

manual:

If all goes well, you should end up with a sub-folder

of your current directory called php4 Simply cd into it

and configure PHP as you would normally, ensuring

that you include with-ncursesto your invocation of

configure, and that you compile the CLI version of

PHP—after all, I’m afraid that you would have little use

for the ncurses extension in a web application

Our First ncurses Application

It’s now time to test whether the installation went

smoothly by executing the simple PHP script that you

can see in Listing 1 This script is designed to be run

from the shell As such, it includes a “shebang”, that is,

a specially-formatted comment (always the first line of

the script), that instructs the shell as to which

inter-preter this file should be executed through This

approach works only on UNIX-type shells, however, so

if you’re running Windows you’ll have to use the

cyg-win shell rather than the DOS command prompt

If you do not receive any errors, you should see thewords “Hello World!” written in red text over a cyanbackground appear at the top of your screen for a fewseconds and then disappear If something goes wrong,there’s a good chance that your shell will become unus-able—don’t panic, this is quite normal, as we’ll see in afew moments

As you can see, even a simple Hello World applicationrequires quite a bit of code if you write it using ncurs-

es You not only have to initialize the library using the

ncurses_init() function, but you have to create acombination, or “pair”, of each foreground and back-ground colours that you want to use in your applica-tion This, in turn, will only work if the terminal you areusing supports colours to start with, and the ncurs- es_has_colors() function provides the script withthat information

Even writing a string to the screen requires a call to aspecialized ncurses function, ncurses_addstr().The latter is actually part of a family of functions thatmake it possible to manipulate text on the screen.Incidentally, you will also have noticed that it’s neces-sary to forcefully refresh the screen in order for anyupdates made to it to become visible This particularaspect of ncurses programming is actually designed sothat only the minimum amount of commands are sent

to the screen, and only when necessary—thus reducingthe amount of data sent to the terminal by as much aspossible With today’s fast connections, this may sound

a bit ridiculous, but you have to understand that

ncurs-es must be able to work efficiently even over slow

seri-al connections—and a bit of optimization never hurts.Thus, for the most part, an application writes to a “vir-tual screen” and then issues a call to

ncurses_refresh()when all of its updates are plete and it is ready to show its output to the user

com-Finally, the ncurses library must be explicitly

terminat-ed by calling ncurses_end() This is necessarybecause ncurses captures the terminal and creates itsvirtual screen over it As a result, exiting from a scriptwithout also terminating ncurses results in a terminalthat, in most cases, is completely unusable

The problem of course, is that sometimes scripts endunexpectedly, as it is often the case when errors occur.For this reason, it’s always a good idea to create yourown error handler that explicitly calls

ncurses_end()before aborting your script This willnot protect you from all kinds of problems (parse errorswill still cause PHP to exit without calling your handler,for example), but it will take care of at least some cases.With the exception of windowing functions, whichwe’ll examine in the next section, all ncurses functionsreturn true if an error has occurred (they actually return

an integer value different from NCURSES_ERR, which isdefined as zero)

cvs -d :pserver:cvsread@cvs.php.net:/repository login

cvs -d :pserver:cvsread@cvs.php.net:/repository co php4

Trang 17

WindowingOne of the most interesting characteristics of ncurses

is the ability to create and manage multiple windows

Although they cannot overlap each other, each window

possesses its own “virtual screen” and can be modified

and refreshed independently of its other counterpart

on the terminal

As an example, let’s take a look at Listing 2, which

introduces a number of new ncurses functions The first

one is ncurses_getmaxyx(), which returns the

dimensions of the virtual screen You have probably

noticed that, in this function, the height is listed in the

function call before the width—something that may

appear unnatural to someone who is used to working

with GUI systems All ncurses functions follow this

con-vention, however, and it does take a while to get used

to it

Also, the call to ncurses_getmaxyx() at the

beginning of Listing 2 uses a constant called STDSCR

This constant describes, by convention, a pointer to the

main virtual screen, which is, for all purposes, a window

of its own right In fact, most of the ncurses functions

that manipulate the virtual screen also have a

window-specific counterpart—for example,

ncurses_addstr() and ncurses_waddstr() In

most cases, the ncurses library implements the

virtual-screen version of these functions as simple macros to

the window versions with the hardcoded STDSCR

parameter

The ncurses_newwin() function is used to create

a new window Its syntax is as follows:

Its result is an ncurses resource that you can use in

subsequent calls to other ncurses functions that can

manipulate the contents of your newly created

win-dow, such as ncurses_wborder, which—you guessed

it—creates a border around the frame of our window:

With the exception of the first parameter, which tifies the window where the border is to be drawn, each

iden-of the other parameters represent the characters thatthe function must use to build the border If you leaveall these parameters as NULL or zero, ncurses_wbor- der() will create a standard (and classy) borderaround the window If, for example, you call the func-tion as follows:

You will obtain a slightly less pleasant—but just asfunctional—border

Writing into a window is performed through a ber of functions The simplest one is

num-ncurses_waddstr(), which is functionally equal to

ncurses_addstr(), with the exception that it takes

a window resource as the first parameter In the case ofListing 2, however, I have used a slightly more complexversion of it called ncurses_mvwaddstr(), which atthe same time moves the cursor to a pre-defined posi-tion and prints out the string passed to it:

As you may have noticed, the x and y coordinatespassed to this functions reference the top-left hand cor-ner of the window—not of the screen There is, natural-

ly, an equivalent of ncurses_mvwaddstr() thatworks with the virtual screen and is called

ncurses_mvaddstr() This, in turn, is functionallyequivalent to calling ncurses_mvwaddstr() as fol-lows:

Finally, we come to the visualization of windows,which is achieved by calling the ncurses_wre- fresh()function Keep in mind that ncurses does nothandle windows the same way a GUI system like

int $x , string $str );

ncurses_mvwaddstr ( STDSCR , $y , $x , $str );

Trang 18

Windows or XWindows would; this means that the

overlapping of windows and features like z-order are

not normally provided to the developer (although

another portion of ncurses called panels, which is also

being implemented in the PHP extension, does provide

exactly this functionality) Thus, wrefresh() should

simply draw a particular window on the screen, as if it

were in focus and with complete disregard for

whatev-er othwhatev-er windows may ovwhatev-erlap with it I say “should”

because in reality different versions of ncurses behave in

different ways—as such, it’s best to either use only tiled

windows, or to use one window at a time on top of the

virtual screen

Thus, calling wrefresh()will cause a window to be

drawn ontop of the virtual screen, in our cases advising

us that its contents are rather messy

Another interesting characteristic of ncurses is that it

provides no scrolling capabilities When you reach the

end of the screen (or of a window), text is simply

dis-carded Depending on the type of application that you

are writing, this could be a major problem—but it can

be easily solved as we’ll see later on

A Bit of Colour and a Sprinkle of Fairy Dust

As marketing people have known for years, colour

makes all the difference In a well-designed interface,

proper colour management can make any application

easier to use (except for vim, which was obviously

writ-ten by aliens) and more pleasant to the eye

The ncurses library provides an extensive set of

func-tions that deal with the aspect of text—not only with its

colour, but also with its intensity and style For

exam-ple, Listing 3 produces text in quite a variety of styles

Because colours can only be rendered on some

termi-nals, the ncurses extension provides the

ncurses_has_colour() function to determine

whether colours can, in fact, be used on the current

ter-minal Also, colour rendering has to be enabled by

call-ing the ncurses_start_color()function

The ncurses library, as I mentioned above, handles

colours by means of “pairs”, each representing the

combination of a foreground and background colour

Each pair is defined using the ncurses_init_pair()

function:

The ncurses extension provides a number of pre-set

colour definitions, which are shown in Figure 1 On

some terminals, it is also possible to create custom

colours by changing the RGB values associated with aparticular pre-defined colour (you could finally haveyour own opportunity to invent the “greenest blue”ever made!) through a call to

ncurses_init_color(), whose syntax is as follows:

A call to ncurses_can_change_color(), whichtakes no parameters, will tell you whether you can,indeed, change the appearance of a particular colour

Attributes are handled in a very similar fashion toHTML They can be turned on using the

ncurses_attron() function (or tron()for a window), or off through a call to ncurs-es_attroff() A list of available attributes is shown inFigure 2

ncurses_wat-Similarly to colours, there is a way to determinewhether a particular attribute is supported by the cur-rent terminal The ncurses_termattrs() functiontakes no parameter and returns a logical OR of all theattribute values that the terminal can handle

A_NORMAL Normal font

A_STANDOUT Standout (reverse)

A_UNDERLINE Underlined

A_REVERSE Reverse screen

A_BLINK Blinking Text

Trang 19

Input Management

As I’m sure you can imagine, a good user interface

without input capabilities is a bit like a war with no

enemy—you could make one but it would be rather

dull and quite pointless The ncurses library offers a

number of facilities for both keyboard and mouse

input, although we will only focus on the latter as far as

this article is concerned

The most commonly used form of keyboard input is

ncurses_getch(), which retrieves a single character

from the input stream Under normal circumstances,

that character is also outputted to the screen, but you

can prevent that from happening by calling the

ncurses_noecho() function For example, take a

look at Listing 4, which shows a simple script that can

be used to control the Apache web server through a

series of simple commands

As you can see, there are a couple of interesting

func-tions First, I’ve created a simple msgbox() routine

that takes an arbitrary amount of text and displays it in

an ordered fashion on a window of its own This sponds to a simple dialogue window in a GUI environ-ment You will notice that I have used the wordwrap()

corre-function to ensure that the text is always properly tered and never interferes with the window’s borders.The lack of a couple of ncurses functions (in particular

cen-wsyncup()and wsyncdown()) in the PHP extensionmake it slightly awkward to manage windowing prop-erly, so that the application is forced to manuallyrefresh the screen every time a dialogue box is dis-played

The ncurses_getch()function is called in an nite loop until the user presses the “q” key and exits.The result value of ncurses_getch() is not, howev-

infi-er, a charactinfi-er, but an integer value This lets you age “special” characters, such as Page-up and Page-down, more easily and conveniently—but you will have

man-to convert the integer value man-to a string if you want man-tointerpret it as a character More complex input can be

Trang 20

performed by calling the ncurses_instr()

func-tion, which reads an entire string from the input and

returns it

It’s interesting, at least in my opinion, to note that

even a simple script like this one can be set as the

default command for a particular user account, thus

depriving it of shell access but still giving it the

oppor-tunity to perform certain operations—in this case,

stop-ping and starting Apache

A More Complex Example

Let’s now move on to a more audacious attempt at

using the ncurses library to produce a complete

shell-based application Listing 5 contains a self-containedtext file viewer that allows for up and down scrolling(both line by line and page by page), can split and dis-play lines of text that span multiple lines (without usingwordwrap, this time) and displays a status line at thebottom of the screen

This script introduces the ncurses_move() tion, which is used to move the cursor to a particularlocation (in fact, ncurses_mvwaddstr() is essential-

func-ly the equivalent of a call to ncurses_move() lowed by a call to ncurses_waddstr())

fol-You’ll notice that the most complicated action formed by this script is the tracking of the position ofthe screen’s output within the text file For the script to

Trang 21

stop scrolling at the end of the file, it is necessary to

track both the position within the file that corresponds

to the top of the screen (which we need so that we can

refresh it), as well as the position that corresponds to

the bottom of the screen This is made more difficult by

the fact that there is no direct correspondence between

the width of the screen and the length of each line of

text, so that a single line of text can in fact correspond

to multiple lines on the screen—but we still want the

script to scroll by one screen line at a time, so that the

script has to track the correspondence between screen

display and file To make things a bit easier, I have

sep-arated the file in its individual lines by using the

file()function

Clearly, it would have been much simpler to load the

file in memory and then use wordwrap() on it to

21 ncurses_init_pair ( , NCURSES_COLOR_RED , NCURSES_COLOR_CYAN );

22 ncurses_init_pair ( , NCURSES_COLOR_RED , NCURSES_COLOR_BLACK );

Trang 22

ensure that there would be a one-to-one

correspon-dence between the screen and the file However, you

can take this script and turn it into a full-fledged editor

with relatively minor modifications—whereas if it were

based on the wordwrap() trick it would be much

more difficult to do so

Where To Go From Here

There is much more to the ncurses library than what

we’ve been able to explore here In particular, recent

versions of ncurses include two libraries, panels and

form, that can be used to manipulate overlapping (and

scrollable) windows and form fields respectively The

panels library is already partially implemented in the

CVS tree of PHP, while there is no support for the formlibrary However, they are both very important, as theycan greatly simplify the development of complex shell-based applications For an example of their full capabil-ities, take a look at the Lynx text-based web browser,which uses both libraries to their fullest potential

22 ncurses_getmaxyx ( $win , $height , $width );

23 ncurses_mvwaddstr ( $win , $y , ( $width - strlen ( $text )) / 2 , $text );

39 if ( $win_w > ( strlen ( $text ) + 4 ))

40 $win_w = strlen ( $text ) + 4 ;

46 if ( $win_h > ( count ( $string_data ) + 4 ))

47 $win_h = count ( $string_data ) + 4 ;

Trang 23

51 $win = ncurses_newwin ( $win_h , $win_w , ( $height - $win_h ) / 2 , ( $width - $win_w ) / 2 );

59 for ( $i = 0 ; $i < min ( $win_h - 4 , count ( $string_data )); $i ++)

60 ncurses_mvwaddstr ( $win , $i + 2 , ( $win_w - strlen ( $string_data [ $i ])) / 2 , $string_data [ $i ]);

91 center_window ( STDSCR , 4 , '(1) Stop Apache' );

92 center_window ( STDSCR , 5 , '(2) Start Apache' );

93 center_window ( STDSCR , 6 , '(3) Restart Apache' );

113 ncurses_init_pair ( , NCURSES_COLOR_CYAN , NCURSES_COLOR_BLACK );

114 ncurses_init_pair ( , NCURSES_COLOR_RED , NCURSES_COLOR_BLACK );

Trang 24

Listing 4: Continued From Page 23

50 // Refresh the screen

Trang 25

auto-minute of down-time costs money Not only personnel cannot perform their work during down-time, but many automated

services, such as telesales, for instance, stop working If an organization monitors its network, it can detect problems before they become a problem, increasing productivity and reducing personnel costs This article provides a brief explanation of the

SNMP protocol and explains the SNMP functionality that is available through PHP.

Simple Network Management Protocol

The Simple Network Management Protocol (SNMP)

provides a framework for network management

SNMP enables access to network management

infor-mation to make, for instance, status monitoring,

fault-detection and configuration possible Since its

intro-duction, SNMP has been used heavily to perform

mon-itoring and fault detection tasks There has been little

use of SNMP for configuration purposes, though, since

it lacks a proper security protocol

However, the latest SNMP framework (version 3)

defines security functionality by means of

authentica-tion, privacy and access control These features protect

the network against threats such as:

1 Modification of an SNMP message in transit by an

unauthorized SNMP entity

2 Modification of the SNMP message stream by

changing the order, delaying or replaying messages

3 Masquerading of SNMP entities through the

appropriation of another SNMP entity’s identity

4 Disclosure of management information that is

transferred between SNMP entities (users)

5 Eavesdroppers that attempt to capture the

man-agement data for other “evil” purposes

6 Unauthorized access to particular management

information

If you compare these capabilities with earlier version

of SNMP, which only offered a single clear text word authentication method and no encryption, you’llsee that SNMPv3 represents a big step forward for theprotocol’s security features What’s more, SNMPv3defines three different security levels:

pass-1) No authentication and no privacy 2) Authentication and no privacy 3) Authentication and privacy

It also allows for the use of different authenticationand privacy protocols, thus enabling SNMP to evolveover time as new and better security mechanisms are

PHP Version: PHP 4 (4.3.0 for SNMPv3 support) O/S: Any

Web server: Any Support Tools: N/A REQUIREMENTS

Trang 26

The SNMP frameworkSNMP is built around an asymmetrical framework ofmany SNMP agents and one (or a few) SNMP man-agers An SNMP agent resides in a remote host ordevice and is connected to the actual hardware thatneeds to be monitored It provides access to the hard-ware via managed objects through an interface known

as Management Information Base (MIB) The agentresponds to queries from managers that request infor-mation from its MIB A manager can therefore create acomplete and coherent picture of the network’s status

by requesting specific management information frommultiple agents

The Management Information BaseThe Management Information Base (MIB) containsthe managed objects under management by an agent.Briefly explained, the MIB is a virtual information storecomposed of one or more MIB modules Each MIBmodule implements a specific set of managementinformation, which is itself organized in an orderedtree

Only the leaves of this tree can contain real data, andthen only if the object/node is defined as an ‘OBJECT-TYPE’ (we’ll get to that in more detail later) The inter-mediate nodes used for creating the hierarchy do notactually contain any information, but provide the treewith a logical structure

Figure 3 illustrates an MIB tree example that is a part

of the SNMPv2-MIB as defined in RFC 1907 (which can

be downloaded fromhttp://www.ietf.org/rfc/rfc1907.txt, if you’re interested

in reading the complete MIB module specification) Thetree depicted in this snapshot contains three objects(sysName, sysContact and sysLocation) representingscalar variables, as well as two objects ordered in a tableunder the sysOREntry node The table objects areordered by column (sysORID and sysORDescr), whileeach row is uniquely identified by an index The index-

es used in Figure 3 are 2 and, therefore, the table tains 4 values (2 columns * 2 rows)

con-Each node in the MIB tree has a human readablename and is ordered by number A specific node isdescribed by an Object Identifier that combines thenumbers of the branches along the path starting fromthe root

To find a particular leaf in the MIB, both an exact and

a next search can be performed The exact searchrequires that the name of the leaf being searched forexactly match what is saved in the data store, while thenext search returns the first lexical graphical higher leaf.Figure 3 depicts a sample walk Each arrow indicates

An SNMP manager requests management information from an SNMP agent The

manag-er invokes an SNMP request on an agent and the agent collects the requested information

from its MIB and produces a response for the manager The manager interprets the returned

information and can use it for logging, status reporting or for automated actions like

initiat-ing alarms A typical example of this would be a pollinitiat-ing system in which periodically traffic

counters are collected by the SNMP manager.

An SNMP agent initiates a notification to a manager Notifications are used when an

agent detects an unusual or anomalous situation on its own, such as when an interface on

the host goes down Upon detecting the fault, the SNMP agent informs directly the SNMP

manager of it, so that the latter can take the appropriate action, such as initiate an alarm.

Figure 1

Figure 2

Figure 3

Trang 27

the “next” of a particular object To indicate that there

are no more “next” values in the MIB tree, an

excep-tion called ‘EndOfMIBView’ is used

Structure of Management

InformationThe language in which MIB modules are written is

called Structure of Management Information (SMI)

SMI was originally based on the Abstract Syntax

Notation 1 (ASN.1) Let’s take a look at a small portion

of this language - just enough to understand the

cre-ation of an MIB tree

The entities defined in a tree must all conform to the

following standard format:

<value identifier> <type> ‘::=’ ‘{‘ <oid> ‘}’

The <value identifier>is used as a human readable

identification of the object/node in the MIB tree The

<type> provides specific information of the object In

the excerpt, all three definitions are of the

‘OBJECT-TYPE’ kind, which includes the syntax of the object, the

type of access granted to it (read/write), its status and

its description

The <oid> provides the hierarchy information

Typically it consists of a single name and a single

num-ber The name indicates the parent node in the MIB

tree and the number indicates the current position in

the tree with respect to its ancestors For example, in

Figure 3, the definitions of ‘system’ has the number 1

and the parent node is ‘mib-2’ The ‘sysContact’ entity

defines the contact information of the particular system

on which the MIB is defined, as mentioned by its

‘DESCRIPTION’ parameter Its parent node is ‘system’

and its own numerical identifier is 4

SNMP and PHPAfter having guided you through a brief introduction

to SNMP and its management framework, it’s now time

to talk about the functionality of the SNMP extension in

PHP The PHP extension has been part of PHP for a long

time, and it was originally developed by Rasmus Lerdorf

as the UCD-SNMP module At that time it supported

the cutting edge version of SNMP—version 1 Over

time, minor modifications have been made to

exten-sion, mainly due to changes in the packages it depends

on, and it is currently called NET-SNMP

Some 3 years ago, I wrote an SNMP extension that

made it possible to take advantage of the security

fea-tures that are part of SNMP version 3 This was never

included in the main distribution for various reasons

However, ever since I returned to Europe and went to

live in Italy, I started working on the extension again

and have included the security features of SNMP

ver-sion 3 in the main distribution for the next release of

PHP

As we have seen in the previous section, SNMP ities can really be divided in two kinds of operations:retrieving information on request from the manager’sside and initiating notifications from the agent’s ThePHP language only supports the first form This is most-

activ-ly for historical reasons, since the first versions of PHPwere used as scripting tools for web-based systems inwhich a notification from the client would simply havebeen impossible to implement: PHP scripts in webpages are only executed upon a web client’s request,and this methodology obviously does not allow for acontinuous listening for notifications In addition, theprobability that a notification be received exactly when

a web page is requested is pretty much zero Thus, theonly SNMP-related commands available in PHP aremanagement information-related Generally they can

be used in polling operations

PHP-SNMP functionsThe SNMP functions in PHP can be divided into twogroups, helper functions and protocol functions Theprotocol functions that we discuss here are those thatexist in versions of PHP that have already been released,and are based on SNMPv1, as are the example scripts.However, the upcoming new release of PHP (4.3) willinclude the new SNMPv3 functions and will supportthe new protocol security features These new functionsare discussed later in this very article

Helper functionsThe helper functions (Figure 4) are used only to alterthe format in which the values passed to and from theprotocol functions are used These functions are onlyfor internal use and do not communicate to externalSNMP agents The only parameter that they accept is aboolean that indicates whether a particular feature is to

be used or not

The example shown in Listing 1 illustrates a script inwhich the helper functions are used to change the out-put resulting from calls to the protocol functions Theiroutput is shown in Figure 5

Protocol functionsThe protocol functions (Figure 6), on the other hand,are used to communicate with the remote SNMPagents They are all used for data retrieval, except for

snmpset(), which can change management data in aremote SNMP agent These functions almost all takethe arguments shown in Figure 7

Listing 2 provides an example of how these functionscan be used in a real-life scenario I have commentedthe script in detail to make it easier for you to follow it

SNMP In PracticeNow that we have introduced all of PHP’s SNMP

Trang 28

functionality, we’ll take a look at a few examples to

show how scripts that allow polling of network

man-agement data from a remote SNMP agent can be

writ-ten

For our first example, we want to create a script that

provides generic system data about the remote server,

such as its hostname (sysName), its location

(sysLocation) and the person responsible (sysContact)

for its administration These are the managed objects

that we have used earlier to explain the MIB tree and

the MIB module syntax Listing 3 contains two

approaches that achieve this result in two different

ways

The first method consists of a series of explicit calls to

snmpget() to retrieve the required information from

the remote SNMP agent This results in 3 requests on

the wire The second approach, on the other hand, is

based on an MIB branch retrieval and subsequent

filter-ing of the information needed This last method results

in a high number of requests being made to the agent,

and does not represent the optimal solution to the

problem, since the entire information tree is retrieved,

even though only three of its values are actually

need-ed The output of Listing 1 is shown in Figure 8

The Interface tableThe second example, shown in Listing 4, retrieves

some columns from a table and demonstrates how

indexes are used The table whose values are being

out-putted is the interface table, or “ifTable”, which is

defined in the IF-MIB module published in RFC 2863(available at http://www.ietf.org/rfc/rfc2863.txt) Thistable maintains information about the interfaces in asystem; an interface is defined as an entry point to thenext underlying layer in a protocol stack A commonexample of this is the TCP/IP stack in network interfacesand serial line connectors

The New SNMPv3 FunctionsRecently, I have added new functionality to theSNMP module so that it can support the SNMP version

3 specifications and, in particular, its security model Allthe protocol functions available in the SNMPv1 modulehave an equivalent in SNMPv3 The only differencebetween the new functions and the old ones is thattheir ‘snmp’ prefix must be changed into ‘snmpv3’.Also, the community password string used for SNMPv1must be replaced by the parameters shown in Figure 9

As you can see in Figure 10, upgrading current scripts

to SNMPv3 is very easy—all you have to do is changethe function names and introduce the new securityparameters The PHP team took this approach toencourage all developers to upgrade their scripts anddeploy the new security features for the management

of their systems

The next release of PHP will also contain two newSNMP helper functions shown in figure 11 I intro-duced them as solutions to problems I encountered try-ing to interpret object identifiers and enumeration val-ues in my scripts

The first function, snmp_set_enum_print(),

caus-es the module to manipulate the data produced bySNMP agents so that, if it is part of an enumeration, its

“human readable” value will be returned rather than itsnumeric one The second,

snmp_set_numeric_print(), causes values

1 <?php

2 /* This script is to show the usage of the helper functions

3 * The script will request the sysName from a remote SNMP

4 * agent by means of an ‘snmpget’ and shows the influence of the

5 * helper function

6 *

7 * Retrieve the value and print it

8 */

9 echo "sysName (quickprint default/’" snmp_get_quick_print () "‘):"

10 snmpget ( "127.0.0.1" , "public" , "SNMPv2-MIB::system.sysName.0" ) "\n" ;

11 /*

12 * Change the quick print format to true

13 */

14 snmp_set_quick_print ( true );

15 echo "sysName (quickprint ‘" snmp_get_quick_print () "‘) :"

16 snmpget ( "127.0.0.1" , "public" , "SNMPv2-MIB::system.sysName.0" ) "\n" ;

17 /*

18 * Change the quik_print format to false

19 */

20 snmp_set_quick_print ( false );

21 echo "sysName (quickprint ‘" snmp_get_quick_print () "‘) :"

22 snmpget ( "127.0.0.1" , "public" , "SNMPv2-MIB::system.sysName.0" ) "\n" ;

23 ?>

Listing 1

Figure 4: Helper Functions

bool snmp_get_quick_print (void)

void snmp_set_quick_print (bool quick_print)

Trang 29

1 <?php

2 /* This script is to show the usage of the protocol functions

3 * The script will request from a remote SNMP agent and finally

4 * do a set on the remote SNMP agent

5 */

6 $target = "127.0.0.1" ; # The address of the remote SNMP agent

7 $community = "public" ; # The community string for access

8 /*

9 * SNMP-GET The php function ‘snmpget’ retrieves a single value

10 * from a remote SNMP agent For instance, to retrieve the uptime

11 * of SNMP agent in a system

12 */

13 echo "\nThe snmpget example\n" ;

14 $result = snmpget ( $target , $community , "SNMPv2-MIB::sysUpTime.0" );

15 echo "The system up time is: " $result "\n" ;

16 /*

17 * The first version of an SNMP-WALK is the ‘snmpwalk’ The following

18 * command fetches all of the managed objects from an agent

19 * The result is returned in an array and contains only the values

20 * To retrieve the values from the array a for-loop is use

21 */

22 $array_snmpwalk = snmpwalk ( $target , $community , "SNMPv2-MIB::system" );

23 echo "\n The snmpwalk example\n" ;

24 for ( $i = ; $i < count ( $array_snmpwalk ); $i ++) {

25 echo $array_snmpwalk [ $i ] "\n" ;

26 }

27 /*

28 * The second version of an SNMP-WALK is the ‘snmpwalkoid’ The following

29 * command fetches only the managed objects from an agent that are under the

30 * specified object identifier and is very usefull in order just to

31 * retrieve a specific branch

32 * The results are returned in an associative array as is done for the snmprealwalk

33 */

34 $array_snmprealwalk = snmprealwalk ( $target , $community , "SNMPv2-MIB::system" );

35 echo "\n The snmprealwalk example\n" ;

36 for ( reset ( $array_snmprealwalk ); $oid = key ( $array_snmprealwalk ) ;

37 next ( $array_snmprealwalk )) {

38 echo $oid " : " $array_snmprealwalk [ $oid ] "\n" ;

39 }

40 /*

41 * The last version of an SNMP-WALK is the ‘snmpwalkoid’ that is an

42 * alias for the ‘snmprealwalk’

43 * The result is the same and ‘snmprealwalk’

44 */

45 $array_snmpwalkoid = snmpwalkoid ( $target , $community , "SNMPv2-MIB::system" );

46 echo "\n The snmpwalkoid example\n" ;

47 for ( reset ( $array_snmpwalkoid ); $oid = key ( $array_snmpwalkoid ) ;

48 next ( $array_snmpwalkoid )) {

49 echo $oid " : " $array_snmpwalkoid [ $oid ] "\n" ;

50 }

51 /*

52 * The SNMP-SET in implemented by the snmpset function This operation can only work

53 * on a exactly specified managed instance and requires extra parameters specifying

54 * the type and the value that you will sent to the remote SNMP agent

55 * For instance, the following command will change the hostname of a system

56 */

57 snmpset ( $target , $community , "SNMPv2-MIB::sysName.0" , "s" , "newhostname" );

58 /* SPECIAL NOTE: The ‘snmpset’ only succeeds if the SNMP agent allows write

59 * access for the community used

60 */

61 ?>

Listing 2

Figure 5sysName (quickprint default/’’):STRING:orville.cardano.lisanza.net

sysName (quickprint ‘1’) :orville.cardano.lisanza.net

sysName (quickprint ‘’) :STRING: orville.cardano.lisanza.netharrie@orville$

Trang 30

returned by a SNMP agent in numeric format not to be

converted into human readable format Numeric values

are probably less intuitive to use for developers, but

they possess the great advantage of remaining constant

and not being subject to change when moving from

one system to another

Tips and tricksMany people dislike SNMP mainly because the do

not understand how to read MIB modules or because

they claim to have too little control over the SNMP

agent Here are a couple of tricks that can make yourlife as a script developer a bit easier

1) Determine first what you want to monitor from a

high-level perspective and what is important for yourdeployment This provides you as a developer with bet-ter guidance and direction and helps not to get lost inthe SNMP details

2) Use tools like libsmi (also open-source, and

avail-able from http://www.ibr.cs.tu-bs.de/projects/libsmi)that can help you by building an MIB tree for yourviewing pleasure If you use the ‘-f tree’ option,smidump will create a summary representation of theMIB modules that are part of a particular tree This willenable you to determine the complete identifier of anyobject quickly and efficiently

3) Use snmprealwalk() on your target agent as atool to help you determine the identifiers of the objectsyou need If you print out the results of a snmpreal- walk() call, you can see what the human-readableform of a particular object identifier is

4) Although this requires a bit more detailed

knowl-edge of SNMP and MIB modules, use the numeric form

of the object identifiers and the new

snmp_set_numeric_print() function to retrievethe numeric form of your object identifiers The advan-

string target

The target is used to define the hostname or the IP address of the remote SNMPagent as well to indicate the port (default 161) number the SNMP agent listens.Some examples are: “hostname” or “hostname:1661” where the first uses thedefault port number and the last the port number 1661 for the SNMP agent onthe system ‘hostname’

string community The community string is an identification of the user/system that wants to access

management information in an SNMP agent

string object_id The object identifier indicates the management information that is requested

from the SNMP agent

int timeout The time out after which the SNMP request is resend if no reply is received

int retries The amount of retries that must be made before giving up

string type and mixed

value

(only used for the snmpset)

The type and the value to which the object identifier in the remote SNMP agentwill be set

string snmpget (string target, string community, string object_id [, int timeout [, int retries]])

array snmprealwalk (string target, string community, string object_id [, int timeout [,int retries]])

bool snmpset (string target, string community, string object_id, string type, mixed value

[, int timeout [, int retries]])

array snmpwalk (string target, string community, string object_id [, int timeout [, int retries]])

array snmpwalkoid (string target, string community, string object_id [, int timeout [, int retries]])

Figure 6: Syntax Protocol Functions

Figure 7

Figure 8

Explicit requests (SNMP-GET)

The system name is: STRING: orville.cardano.lisanza.net

The system administrator is: STRING:

<webmaster@your.domain>

The system location is: STRING: Cardano,Italy

Tree retrieval, value selection (SNMP-WALK)

The system name is: STRING: orville.cardano.lisanza.net

The system administrator is: STRING:

<webmaster@your.domain>

The system location is: STRING: Cardano,Italy

Trang 31

1 <?php

2 /* This script will request the sysName, sysContact and sysLocation

3 * from a remote SNMP agent in two different approaches and prints the

4 * results

5 */

6 $target = "127.0.0.1" ; # The address of the remote SNMP agent

7 $community = "public" ; # The community string for access

8

9 /*

10 * The first approach:

11 * explicit retrieval of the wanted managed instances (SNMP-GET)

12 */

13 echo "Explicit requests (SNMP-GET)\n" ;

14 $name = snmpget ( $target , $community , "SNMPv2-MIB::system.sysName.0" );

15 $contact = snmpget ( $target , $community , "SNMPv2-MIB::system.sysContact.0" );

16 $location = snmpget ( $target , $community , "SNMPv2-MIB::system.sysLocation.0" );

17

18 echo "The system name is: $name \n " ;

19 echo "The system administrator is: $contact \n " ;

20 echo "The system location is: $location \n " ;

21 echo "\n" ;

22 /*

23 * The second approach:

24 * The complete system subtree is retrieved first and is followed by

25 * selection of the needed information

26 */

27 echo "Tree retrieval, value selection (SNMP-WALK)\n" ;

28 $group = snmpwalkoid ( $target , $community , "SNMPv2-MIB::system" );

29 $name = $group [ "SNMPv2-MIB::sysName.0" ];

30 $contact = $group [ "SNMPv2-MIB::sysContact.0" ];

31 $location = $group [ "SNMPv2-MIB::sysLocation.0" ];

32

33

34 echo "The system name is: $name \n " ;

35 echo "The system administrator is: $contact \n " ;

36 echo "The system location is: $location \n " ;

37 ?>

Listing 3

Figure 9

string username The username identifies who wants access a remote SNMP agent This could be

compare to the old community string of SNMPv1/v2C

string sec_level

The security level defines the grade of security and only the following levels exist:

- ‘noAuthNoPriv’ or ‘nanp’; a security level that is similar to community-basedsecurity This level does not require any of the authentication and privacyparameters to be set and therefore must be provided as “” empty strings

- 'authPriv' or 'ap'; the highest security level that provides authentication andencryption This level does require both the authentication and the privacyparameters to be set

string auth_protocol The authentication protocol defines the sort of authentication used The

follow-ing protocols are supported 'MD5' and 'SHA'

string auth_passphrase The authentication passphrase is used to generate an authentication key with

which the authentication is done

string priv_passphrase The privacy protocol defines the encryption protocol and the following protocol

is currently only supported ‘DES’

string priv_passphrase The privacy passphrase is a secret phrase to be used to generate an encryption

key

Trang 32

tage of using this approach is that numeric identifiers

are unique in the MIB tree, and they never change—

human readable forms may be modified

5) Search for and read RFCs and books on SNMP For

instance, a good book with more in-depth analysis of

the MIB modules is “Understanding SNMP MIBs” by D

Perkins and E McGinnis, et al (published by Prentice

Hall), while a good book for the SNMP protocol is

“SNMPv1, SNMPv2c, SNMPv3 and RMON”, written by

W Stallings and published by Addison-Wesley

6) Last but not least, don’t be frustrated if you don’t

get everything working right away Take your time andwork through your problems one at a time

1 <?php

2 /* An example script that prints the type of interfaces with their

3 * descriptions within a system The output format is in HTML format

4 * Set the quick print to retrieve clean values without type information

11 * One cannot assume that all the indexes are incremental

12 * Gaps between ifIndexes may exist

13 */

14 $ifIndex = snmpwalkoid ( $target , $community , "RFC1213-MIB::ifIndex" );

15

16 /* Data retrieval executed by explicit requests to the remote SNMP agent */

17 $ifDescr = array(); # Define the description variable as an array

18 $ifType = array(); # Define the type variable as an array

19 /*

20 * Cycle trhough the earlier retrieved index values and create from

21 * the object identifier specifying the column and the index the

22 * precise object identifier needed

23 */

24 for ( reset ( $ifIndex ); $oid = key ( $ifIndex ) ; next ( $ifIndex )) {

25 /* Get the index value */

31 /* Retrieve the data and insert it in the description array */

32 $ifDescr [ $i ] = snmpget ( $target , $community , $var );

33

34 /* Concatenate the column object identifier with the index */

35 $var = "RFC1213-MIB::ifType." $i ;

36

37 /* Retrieve the variable and insert it in the type array */

38 $ifType [ $i ] = snmpget ( $target , $community , $var );

39 }

40 /*

41 * Print the retrieved data in a table

42 * We do this by cycling through the ifIndex array getting the

43 * index values and then using the index value to get the

44 * description and the type of the interface

Trang 33

Signing OffNow that we have briefly introduced SNMP its relat-

ed PHP functionality, I think that it’s fair to say that PHP

is a simple and rich language for creating SNMP scripts

Due to the wide variety of extensions available for PHP,

such as those that allow for database and graphics

functionality, the potential is there to write complex

management applications that provide very advanced

capabilities In fact, there are already two SNMP

open-source packages available at www.open-sourceforge.net—

Just For Fun Network Management and PHP NetworkAnalyzer—that make complex network analysis andmanagement with PHP a reality

string snmpv3get ( string hostname, string username, string seclevel, string auth_protocol, string

auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int

retries]])

array snmpv3realwalk ( string host, string username, string seclevel, string auth_protocol, string

auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int

retries]])

bool snmpv3set ( string hostname, string username, string seclevel, string auth_protocol, string

auth_passphrase, string priv_protocol, string priv_passphrase, string object_id, string type, mixed

value [, int timeout [, int retries]])

array snmpv3walk ( string hostname, string username, string seclevel, string auth_protocol, string

auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int

Revolving around YOUR business

Designed for PHP Programmers Virtual Private Servers & Dedicated Servers

Reliable Internet hosting solutions Guaranteed 99.95% uptime

Harrie Hazewinkel is a long time participant in various open-source projects He is the author of MOD-SNMP—an SNMP module with which the status of Apache can be mon- itored He is an active participant in the Internet Engineering Task Force (IETF) and is the editor of the stan- dard that defines managed objects for WWW services His main expertise is Network Management and World Wide Web applications Currently, he is an independent consultant and

is open for new Internet challenges and adventures You can reach him at harrie@lisanza.net.

php|a

void snmp_set_enum_print (bool enum_print)

void snmp_set_numeric_print (bool numeric_print)

Figure 11

Trang 34

Ever since PHP came on the market, proclaiming itself

to be the world’s most beloved programming

lan-guage, there has been a lot of disappointment over its

weak cross-database portability Indeed, for many

developers, supporting pretty much all the existing

databases through individual modules was simply not

enough Developers also want the possibility to avoid

hardcoding db-specific functions in their applications—

they want to call only one module (or object), tell it

what DBMS to use and access their data transparently

As it turns out, that wasn’t a reasonable addition to

PHP—simply because of its nature First of all, PHP is

not a true object-oriented language—it uses

procedur-al functions to interact with DBMS systems, and

libraries for different databases have to be called in

dif-ferent ways Second, DBMS extensions are, internally,

independent of each other—it would thus be almost

impossible to synchronize them into one single object

without first creating an OOP wrapper, which brings us

back to the initial problem

PHPLIB

As PHP evolved, PHPLIB came into existence PHPLIB

was a PHP library whose main goal was to allow for

ses-sion management in PHP3 Sesses-sion information was

saved in a database and, with the intent of providing

cross-database functionality, a transparent layer was

used for data access Not only you could save sessions

into your favorite DBMS, but there was also a way for

you to directly access these databases from your scripts,thus making PHPLIB the very first data abstraction layeravailable to PHP users

In PHPLIB, you’d call your database with somethinglike this:

This example would print an HTML table filled with

results Handy, isn’t it?

Before you could get away with accessing your base using just five lines of code, however, you’d have

data-to specify what DBMS should have been used via anincluded file That was convenient for most cases—par-ticularly for projects where the DBMS could havechanged at some point in the future If that “luckyevent” were to happen to you, you’d simply have to

Layers

By Maxim Maletsky

What is all this Database Abstraction fever? Why do we make so much noise about it and create so many different

solu-tions, none of which seem to be able to make everyone happy? Perhaps, because not everyone tried to understand what the

actual goal for abstracting database access is.

PHP Version: 4.0 and up O/S: Any

Web server: Any Support Tools: A Database System supported by PHP REQUIREMENTS

Trang 35

edit the main PHPLIB configuration file to point to a

new database configuration file Besides sessions and

DB functionality, PHPLIB also provided you with lots of

other goodies, such as an authentication system, HTML

rendering classes and much more

Unfortunately for PHPLIB (and luckily for all of us),

PHP4 came out with sessions incorporated by default,

making them simpler to use and allowing for the full

cross-platform compatibility with no need to use any

database at all that everybody was waiting for When

that happened, PHPLIB started becoming less and less

popular—to the point that it was used just for its DB

abstraction capabilities and, of course, the other

“goodies” that people were so addicted to Alas, even

that didn’t last for too long

PEARSoon after PHP4 was released, the PEAR project

began PEAR is a library similar to PHPLIB, with the

all-important exception that it was developed officially

along the PHP core itself It contains an extremely

pow-erful set of database abstraction functionality—as well

as many more useful tools There are dozens of people

who are working on PEAR today and thousands of

peo-ple who use it

One of the main goals of PEAR is to provide PHP with

a flexible database access system available on any

plat-form (and for any DBMS) An example of database use

with PEAR is shown in Figure 1

The PEAR DB class makes it quite easy to work with

multiple databases PEAR also allows you to use Data

Objects so you can build very dynamic queries and

connect several of the other available PEAR

compo-nents to it For instance, if you wanted to create a query

which selects all the employees who are between 18

and 35 years old, with Data Objects you could

dynam-ically build it this way:

This would compose and internally execute the

fol-lowing query:

More DB Abstraction ModulesBesides PHPLIB and PEAR, there are also a couple ofother built-in extensions to PHP that allow for databaseabstraction, such as DBA and DBX They are fast andflexible, but somewhat lacking in the compatibilitydepartment There is also ADODB—a great and verypowerful database library that is now available on anumber of platforms

What the Ideal DatabaseAbstraction Layer Should DoAll these Database Abstraction Layers miss (at least)one thing Sure, they do let you switch easily betweenall kinds of DBMS systems The problem is that data-base systems do not simply differ in the way PHP inter-acts with them: they also often use a particular

“dialect” of the SQL language that differs from thestandard in subtle but significant ways Thus, eventhough, theoretically, the word “Abstraction” between

“Database” and “Layer” means you can accomplish thesame task on any database server without editing a sin-gle line of your code, this is not entirely possible todaybecause you will still have to modify your queries

For example, assume for a moment that we need tofind out my age through SQL having only a string con-taining my birthday In MySQL, the code will look likethis:

In PostgreSQL, however, that will not work, and willhave to be changed as follows:

Oracle, on the other hand, requires quite a bit morework, as shown in Figure 2

All three SQL databases I used in the example claim

to fully support SQL standards The problem is, tunately, that the SQL standard is somewhat limitedand not always adequate to the actual needs of everydeveloper—as a result, you’ve just had quick proof thatmost DBMS systems do not (and, likely, never will)share the same SQL standard at all Thus, if you’re used

unfor-to having your SQL queries hardcoded in your

applica-1 <?php

2

3 $employee = new DataObjects_Person ;

4 $employee -> whereAdd ( ‘age > 18’ );

5 $employee -> whereAdd ( ‘age < 35’ );

6 $employee -> find ();

7

8 while ( $employee -> fetch ()) {

9 echo " $employee -> id } { $employee -> name } <BR>" ;

- YEAR ( ‘1978-10-26 20:38:40’ ) ) as age;

SELECT ( EXTRACT( YEAR FROM TIMESTAMP ‘now’ ) -

EXTRACT( YEAR FROM TIMESTAMP

‘1978-10-26 20:38:40’ ) ) as age;

Trang 36

tion, you are going to have a very hard time rewriting

most of them if you need to migrate it from one

data-base to another

Ideally, a good Database Abstraction Layer should

support differences in the SQL dialects for you One

way of doing this would be to dynamically rewrite the

SQL code right within the db layer (such as ADODB

does in some cases) However, as you can already

imag-ine, this isn’t always a good solution, even if many DB

layers try to implement it For one thing, it’s not always

possible to convert a SQL string written for a particular

DBMS into its equivalent for a different DBMS in a

uni-vocal way; this means that it would be difficult (and

dangerous) for the DB layer to “guess” what a SQL

query means in every possible case Another reason,

and perhaps the most crucial one, is that parsing,

“rethinking” and “repairing” every single SQL

com-mand would cause a significant performance hit that is

not easily justified The “good” database abstraction

layers that we have today already slow things down

enough—even without this feature

Finding a solution to this problem may look like an

impossible dream In reality, all it takes is a bit of

fore-sight and some level of discipline All you have to is to

collect and save your SQL queries in a central location

and use a sound dynamic mechanism to load them into

your code as needed

Consider the code in Listing 1—let’s call itmysql.sql.php—in which you hold your queries As youcan see, a separate variable is required for each SQLstatement Its value will later be injected into a call to adatabase, resulting in a system that can easily adapt toany DBMS—all you would have to do is to createanother file, say postgresql.sql.php, that contains theSQL syntax appropriate for the DBMS you want to use

Of course, you will need a little framework that canuse these files, providing you at the same time with the

Theoretically, the word

“Abstraction” between

“Database” and “Layer”

means you can accomplish the same task on any data- base server without editing

a single line of your code.

“Dearly Beloved, we are gathered here today to mark the passing of a fine young software product So rich in promise,

so beloved by its users, so carefully tended by its developers ”

rap-x is the collaboration platform that enables software developers to generate revenue from their existing users.

Click for a demo at rap-X.com, or email patrick.dobbs@laculine.com for more information

rapX, don’t R.I.P.

Ngày đăng: 21/12/2013, 12:15

TỪ KHÓA LIÊN QUAN

w