The most comfortable keyboards in production have a “split” ergonomic design, but if you don’t touch-type, they can be too difficult to learn how to use. The Microsoft Wireless Comfort Keyboard makes a compromise for standard keyboard lovers: it’s only slightly curved, so you can have some extra comfort without having to make dramatic changes in your typing habits. Pair that with one of the industry’s most comfortable and accurate mice, and you have an unbeatable desktop set. If you’ve thought about replacing your OEM keyboard and mouse with something similar but more comfortable, this might be the package that convinces you. Read more at Hardware in Review or Discuss this article or get technical support on our forum.
April 24, 2006
Hacking OpenBSD 3.9
Many people responded to the call for OpenBSD and OpenSSH donations by purchasing an OpenBSD CD set. Those CDs are beginning to arrive in the mail, and when they do, how are you going to use them? If you’re a software enthusiast who has never used OpenBSD before, you might enjoy installing it by yourself and figuring it out as you go. If, however, you’re looking for a more practical approach to using OpenBSD as a desktop or server operating system, here’s a guide to get you started.
Secure by default
First of all, you should familiarize yourself with the concept of secure by default. A simple way of explaining it is, everything is turned off until you turn it on. That means that the Web server is not going to start until you manually add httpd to the startup script. OpenSSH services will also be unavailable unless specifically enabled.
Because it is secure by default, you may have to do more initial configuration with OpenBSD than with most other Unix and Unix-like operating systems, but you’ll spend a lot less time securing it — maybe no time at all, if you follow the instructions in the manual pages.
Quick OpenBSD facts
- Default shell: Korn shell (ksh) for root; Bourne shell (sh) for users; the C shell (csh) is also included by default.
- Default editor: vi
- File system: BSD Fast File System (FFS) with soft updates (no journalling necessary)
- Kernel: 4.4BSD-based, monolithic, SMP-capable, does not support external kernel modules by default
- Binary support: OpenBSD, FreeBSD, SCO/ISC, SVR4, Linux, BSD/OS
- Supported architectures: Alpha, AMD64/EM64T, cats, hp300, hppa, i386, luna88k, mac68k, macppc, mvme68k, mvme88k, sgi, sparc, sparc64, vax, zaurus
- Hardware support (i386)
Making it easier to mount CD drives
First you need a mount point for your CD or DVD drive. I recommend /mnt/cdrom (create it with mkdir /mnt/cdrom), but you can do whatever you want — just remember what it is so you can mount the CD drive on it later, and modify the below instructions accordingly.
OpenBSD’s default /etc/fstab file does not have a line for CD/DVD drives. Most people’s optical drive will use the /dev/cd0a device node, though there are a few others. Type ls /dev/cd* to see all of the possibilities. You may want to test mount them with a CD in the drive if you are unsure which node is the right one.
Once you have a directory to mount to and you know which device node corresponds with your optical drive, it’s time to add a line to /etc/fstab:
/dev/cd0a /mnt/cdrom cd9660 ro,noauto,nosuid,nodev 0 0
Setting up Ports and packages
OpenBSD doesn’t include much software in the default system, so you’ll probably have to add most of the programs that you need. There are two ways to add software to OpenBSD: through the Ports tree, and through precompiled binary packages. Neither is necessarily better than the other, but here are some basic observations about both systems that will help you decide which approach to take:
- Ports compiles each program from source code, which allows you to modify the Makefile to accommodate specific needs; packages are already compiled with the default options.
- Packages are installed moments after they are downloaded; Ports can take a long time to compile.
- Packages are easier to upgrade when it comes time to switch to the next OpenBSD release; Ports are trickier to upgrade, and will take much longer to reinstall.
- There are about 200 more programs in Ports than there are in the package repository. Many of these extra programs are proprietary (the Java Development Kit, for instance).
- It’s easier to find programs in Ports than it is the package database, especially when you’re offline. You can, however, use the Ports tree to find a program you want to install, then use
pkg_addto install the package.
My recommendation is to install the Ports tree (see below for instructions), but use packages whenever possible. The two may be used in conjunction with each other (see below), but if you do not install the Ports tree you will have to know the exact package names, as OpenBSD package tools do not use any kind of name resolution. That means you can’t just type pkg_add gnome and have GNOME installed. You have to know the exact package name, which has its version number and patch level appended to it. Since you probably don’t know exactly what version and revision of GNOME is available for the current release of OpenBSD, you’ll have to use your OpenBSD CD to browse the package list. Alternatively you can connect to the OpenBSD FTP server and search through the same list online.
Mount your OpenBSD CD and then switch to its directory so that you can browse it. Assuming you mounted it on /mnt/cdrom, the i386 package directory is in /mnt/cdrom/3.9/packages/i386/ (assuming you’re installing packages in OpenBSD 3.9 — if not, change the version number in the path). Use the ls command to look through the directory and find programs that you want to install.
Next you’ll need to tell your package installer where to look for package files. By default it takes a command line argument, so you have to specify an address and file name for every package you want to install plus all of its dependencies. Obviously that is not a very efficient way to do things, so let’s add a default path for the pkg_add command to look in.
Initially you may want to use the OpenBSD CD because it’s quick, available, and doesn’t require an Internet connection. It doesn’t have all of the OpenBSD packages on it, though — just the ones there was space for. If you want access to more packages, you’ll need to use the FTP site (detailed below). To add the CD as the default package location, use vi to open /root/.profile and then add these two lines at the bottom:
export PKG_PATH=/mnt/cdrom/3.9/packages/i386/
export FETCH_PACKAGES=yes
The “3.9″ and “i386″ will change depending on the release of OpenBSD you’re using and the architecture of your computer. If you’re installing OpenBSD 3.8 or 4.0 on an AMD64 or VAX machine, change the directories accordingly. Before you install any packages, make sure the correct CD is in and mounted.
If you want to install packages at a later time and don’t want to lug around your OpenBSD CDs, or if you didn’t find the programs you want on the CD, you can use an FTP package mirror instead. First find a mirror in this list that is closest to your location. Then add it to /root/.profile as shown above:
export PKG_PATH=ftp://ftp2.usa.openbsd.org/pub/OpenBSD/3.9/packages/i386/
The mirror site above is only an example — use one from the list I linked to above.
Log out for the changes to take effect. The next time you log in, pkg_add will automatically retrieve any packages you tell it to, plus their dependencies. If you try to install a program from Ports, OpenBSD will check to see if there is a package available first; if so, it downloads that instead (to remove this feature, comment out the FETCH_PACKAGES line in your profile). Here’s an example:
pkg_add -v gnome-desktop-2.10.1.tgz
The -v flag makes the output more verbose; it is not required. In the above example, GNOME and all of its dependencies will then be downloaded and installed on OpenBSD 3.8.
After you’ve added a package source, whenever you try to install a program from Ports, OpenBSD will automatically try to retrieve the package first. So even though there is no name resolution for packages, Ports can act in that regard.
Adding the Ports tree and OpenBSD source code
OpenBSD does not install the Ports tree or the operating system source code by default. To install them yourself, just copy them over from CD #3 or download the source files from the OpenBSD FTP site. You can find them in the /pub/OpenBSD/3.8/ directory (substitute 3.8 for your release version). The files are called src.tar.gz and ports.tar.gz.
Unzip and untar the src.tar.gz file to the /usr/src/ directory, and the ports.tar.gz file to the /usr directory (it will unpack to a new /usr/ports/ directory). That’s basically all there is to it.
Java support
Installing a Java Development Kit on OpenBSD is more difficult than on most other OSes. On the other hand, most other OSes don’t really care about licensing to the degree that OpenBSD does. Since proprietary packages cannot be included with OpenBSD, you’ll have to use the Ports tree to install the JDK. There is currently no option to install a standalone Java Runtime Environment without the development kit.
To install a JDK (and by association, a Java Runtime Environment as well), first you’re going to have to manually retrieve the JDK binaries, source code, and BSD patch sets from a few Web sites, then you’re going to have to compile them from source. It takes a long time, so I suggest fetching the files all at once, then letting OpenBSD work on compiling them overnight. This process also requires a lot of free space in /tmp and /usr, so make sure you’ve got some room to work with. The amount of free space necessary depends on which JRE or JDK version you are installing. At minimum, a few hundred megabytes; at maximum, maybe more than 1GB. The reason why you need so much disk space and compile time is, Java must bootstrap from a previous version. That means that JDK 1.5 bootstraps from 1.4, which bootstraps from 1.3. So you’re downloading files for and compiling three JDKs. That is, unfortunately, the price you pay for using Java on OpenBSD.
The file names, versions, and addresses will change with every release. A sure-fire way to find out what files require manual fetching is to go to /usr/ports/devel/jdk/1.5 (assuming you want Java 5.0 — versions 1.3 and 1.4 are also available) and type in make. Any initial dependencies will be fetched and compiled, and when it reaches a point where your intervention is required, the exact names and Web addresses of the files you need to retrieve will be printed on the screen. Go to the addresses, download the files, and save them to /usr/ports/distfiles/, then continue the build. If you miss a file or two, the build process will tell you which files you’re missing.
As an example, here are the file names and addresses for JDK 1.5 on OpenBSD 3.9:
- j2sdk-1_3_1_16-linux-i586.bin from http://java.sun.com/j2se/1.3/download.html
- j2sdk-1_4_2-linux-i586.bin from http://java.sun.com/products/archive/j2se/1.4.2/
- j2sdk-1_4_2-src-scsl.zip, j2sdk-1_4_2-bin-scsl.zip, j2sdk-1_5_0-bin-scsl.zip, and j2sdk-1_5_0-src-scsl.zip from http://wwws.sun.com/software/communitysource/j2se/java2/download.html
- bsd-jdk14-patches-7.tar.gz and bsd-jdk15-patches-2.tar.bz2 from http://www.eyesbeyond.com/freebsddom/java/jdk14.html
Lastly, you will have to add the Java executable path to your shell configuration file. Assuming you are using the default Bourne (sh) or Korn shells (ksh), the file to edit is ~/.profile. If you’re using the C shell (csh), the file is ~/.cshrc. Bash is ~/.bashrc, and the Z shell (zsh) is ~/.zshrc. Somewhere in one of these files you will find a PATH environment variable. Add /usr/local/jdk-1.5.0/bin to it (or whatever Java version you installed). Some programs may require a JAVA_HOME setting as well:
export JAVA_HOME=/usr/local/jdk-1.5.0/
Log out for the changes to take effect. Remember to go to the /usr/ports/devel/jdk/1.3 and 1.4 directories and run make deinstall clean to remove the older JDKs and build files that you bootstrapped from.
Enabling FreeBSD and Linux binary support
OpenBSD comes with a variety of binary compatibilities compiled into the kernel. They are, however, disabled by default. To enable them, edit /etc/sysctl.conf and skip down to the end of the file where the binary emulation section is. Uncomment any lines that you need support for. Most people will want Linux binary support:
kern.emul.linux=1
FreeBSD binary support is in the same section. Again, just uncomment it to enable it. Feel free to look through the rest of the file to see if there are any other options you might be interested in (I usually enable wsmouse, which is the console mouse driver).
To achieve optimum Linux binary compatibility, you will also need to install the redhat_base package, then create a /proc directory and a line in /etc/fstab to mount it at boot:
/proc /proc procfs rw,linux 0 0
Recompiling the kernel
I’ll start this section by saying that you probably won’t ever need to do this. Even if you often find yourself messing with Linux or FreeBSD kernel options, you’re likely to never need to mess with OpenBSD’s — pretty much everything is compiled in by default. Some say that the fewer kernel options you have (in other words, taking out what you don’t need), the better the kernel performs (or at very least, the smaller it is), but I haven’t done any performance testing to verify that.
The kernel configuration files are in /sys/arch/i386/conf (substitute i386 for your architecture if it is different). The standard kernel config is in the GENERIC file. If you want to compile a custom kernel, I recommend creating a separate file based on GENERIC rather than screw around with the original. Traditionally a custom config file is called MYKERNEL — so just copy GENERIC to MYKERNEL and edit from there. The SMP kernel options are in GENERIC.MP. If you’re on an SMP machine, don’t bother editing the file — it only contains a few SMP-specific options that override GENERIC — just skip to the next step.
Once you’ve got your configuration the way you want it, run the config program on it:
/usr/sbin/config MYKERNEL
If errors are detected, fix them and re-run config. If no errors are detected, switch to the directory that config created:
cd ../compile/MYKERNEL
Then compile the kernel:
make clean && make depend && make && make install
SMP support for multi-core, multi-CPU, and Hyper-Threaded machines
If you’re on a multi-core or multi-CPU system and want to use the SMP kernel, you do not need to recompile anything to get SMP support. While OpenBSD uses the single-CPU kernel by default, you have the option of installing the bsd.mp kernel during the installation process. If you choose that option, bsd.mp will be in your / directory.
Before you switch to bsd.mp, test it out by typing it in at the boot prompt when the system starts (before OpenBSD starts its init process). If all goes well, just switch to your root directory and move bsd.mp to bsd:
mv bsd.mp bsd
Further information
OpenBSD has the most thorough, easy to follow documentation of any operating system I’ve ever used. Just use the man command to look up nearly anything that is included with the base system or installed packages. If you’re new to OpenBSD, type man afterboot to get some tips and instructions for setting up and configuring various services and devices.
If you’re still stuck after reading the documentation, a great source for online BSD help is the OpenBSD section of the BSD Forums Web site.
Discuss this article or get technical support on our forum.
Copyright 2006 Jem Matzan.
|
|
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License.
|
There are two main points to make about this situation — or rather, what is implied in this situation. The first is that every company that switches to Microsoft products is not against Linux, free software, or any of the other pro-freedom, anti-Microsoft factions or communities. It’s a business deal, and one that I bet Microsoft sweetened with discounts so deep that you might wonder why they bother selling software at all. Examine the situation: GoDaddy is the world’s largest domain registrar, and switching over their parking service to Microsoft means a more than 5% market share jump in the Netcraft monthly survey. If you were a rich corporation like Microsoft, wouldn’t you do everything in your power to get that huge chunk of market share in one fell swoop? I won’t lie — I’d do it.
As an aside, these are parked domains. Parked as in “not in use right now, and might never be.” Parked as in when they go live someday, they will be hosted on a real Web server that may or may not be running Windows.
The second main point is that free software isn’t Linux. The Linux kernel may be free software, but it’s not the only show in town. It’s several times smaller than the OpenOffice.org codebase and smaller than each of the BSD operating systems and OpenSolaris — all of these are free software projects. If GoDaddy had switched to FreeBSD from GNU/Linux, would there still be an uproar about it from the Linux peanut gallery? I think there probably would be, but perhaps on a different scale and with different people involved.
I can’t help but think that this GoDaddy donation is partly just PR meant to offset the impression that they are pro-Microsoft on account of this domain parking deal. They don’t want to lose customers that want to host on GNU/Linux, like EV1 did when they bought one of SCO’s Linux licenses a few years back. I can understand that, and to a certain extent, the motive behind the donation doesn’t really matter. What matters is, OpenSSH is getting some corporate donations from companies that realize the benefit of monetary support of free software projects. This is the second such donation, the first having come from the Mozilla Foundation a few weeks ago. Another 8 more $10,000 donations and OpenBSD/OpenSSH will have reached its operational funding goal of $100,000.
Sources
Discuss this article or get technical support on our forum.
Copyright 2006 Jem Matzan.
|
|
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License.
|
Writing analysis
At more than 1500 pages, this book is a real behemoth. Many of those pages are consumed by example programs and code snippets that you’ll only glance at or spend a few minutes analyzing. The author’s code is also peppered with superfluous comments that, according to him, are a side-effect of an automatic function that retrieves code from his server and inserts it into the book layout so that he doesn’t have to do it manually.
Speaking of the code examples, they are terribly designed, especially the ones in the first few chapters. Instead of using sensible variable and method names, the author uses letters — sometimes many of them per code sample, which makes it impossible to follow in your head. He also likes to include his own homebrew libraries, using their contents so liberally that his examples are unusable without them. It’s better to read and take notes on what he says, skip his horrible code examples (except to see the one or two lines that show a new concept in action), and do the exercises.
The author is generally easy to read and understand, and he has an excellent grasp not only of proper object-oriented programming and Java language conventions, but of how to take advantage of the new features of Java 5.0. Although I did not read any previous editions of this book, I assume that they did not cover JDK 5.0 (1.5). It’s probably not worth buying the 4th edition if you’ve already got a previous edition and are familiar with Java. Thinking in Java is not really a reference text — it’s more for instructional purposes.
Putting the book to the test
Thinking in Java, 4th Edition plunges right into Java and object-oriented design principles, and maintains its rapid pace throughout the rest of the book. What separates Thinking in Java from its competitors is the inclusion of reader exercises. Integrated into each chapter (or at the end of the chapter, in some cases) are programming exercises of varying degrees of complexity. Each exercise asks the reader to design or modify a program that uses a recently-covered Java concept. So instead of just taking notes and answering questions, you actually have to sit at a keyboard and prove that you can use the language you’re learning. In all of the years that I’ve spent learning languages (both for programming and for speaking), I have never had much success without doing exercises in this fashion.
While this book uses a superior approach to teaching Java, it does not hold the reader’s hand or repeat complex concepts in oversimplified terms. In other words, be prepared to fully engage your brain if you’re going to use Thinking in Java, 4th Edition to teach yourself how to program in Java.
Some of the exercises accidentally ask about concepts that have not yet been introduced in the book at the time of their asking. Other than that, I found no major problems or errors in Thinking in Java, 4th Edition.
Conclusions
As I mentioned above, Thinking in Java, 4th Edition is not for the casual learner — you have to concentrate on the material and be motivated to learn it. The same could be said of practically any book on any kind of language, but this one doubly so because of its fast pace.
I highly recommend Thinking in Java to anyone who learned a programming language in high school or college and wants to get into hobbyist programming in a modern object-oriented language. You don’t need any object-oriented experience — the book will teach you what that’s all about — but you will need to at least understand the basics of procedural programming in C, C++, Pascal, Python, Perl, PHP, Lisp, or some other high-level language. Alternatively, you might also buy Thinking in Java with O’Reilly’s Head First Java to help you understand some of the complexities of the language.
Discuss this article or get technical support on our forum.
| Title | Thinking in Java, 4th Edition |
| Publisher | Prentice Hall |
| Author | Bruce Eckel |
| ISBN | 0131872486 |
| Pages | Paperback, 1520 pages |
| Rating | 8 out of 10 |
| Tagline | The definitive introduction to object-oriented programming in the language of the World Wide Web. |
| Price (retail) | U.S. $38. Buy it from Amazon.com |
Copyright 2006 Jem Matzan.
|
|
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License.
|
Transferring single objects
If you only have a handful of individual files or directories to transfer, it’s probably easiest to send them one at a time via secure shell (OpenSSH). Every GNU/Linux distribution includes OpenSSH, but not all of them run the SSH daemon (which allows you to connect to the computer via SSH) by default. Starting the daemon is as simple as typing sshd in a root terminal. You can also configure your distribution to start OpenSSH at boot time; each distro handles startup scripts differently, so you’ll have to consult your distro’s documentation to learn how to do that.
Once the secure shell daemon is started, other computers can connect to your machine from a terminal window by using the ssh command, or you can copy files over a secure connection by using scp. The latter works just like the regular cp command does, except you have to give it an address for at least one of the files:
scp picture.jpg 192.168.1.101:/home/user/pictures/
In the above example, a file called picture.jpg is transferred to the network machine with the address 192.168.1.101 in the /home/user/pictures/ directory. By default, scp copies the target file or directory to the home directory of whatever user you’re logged in as; you can specify a different location by adding the path after the colon following the address.
By using your /etc/hosts file, you can create a nickname for the remote machine you’re copying files to. Just start a new line in the file, type in the IP address of the machine you want to nickname, then press the tab key once and type in the name you’d like to call it:
192.168.1.101 laptop
In the next example, we’ll use the laptop nickname instead of the address, specify a different user than the one currently logged into the terminal we’re copying from, and copy an entire directory instead of just one file:
scp -r /home/user/pictures/ user2@laptop:/home/user2/
The -r switch means recursive, which tells scp to copy the directory and everything in it. The above command will create a pictures directory to the /home/user/ directory on the laptop computer, and copy all of the contents of the local machine’s /home/user/pictures/ directory to it. So what do you do if the remote machine already has a /home/user/pictures/ directory, but you still want to copy everything in the local machine’s pictures directory? You use a wildcard:
scp /home/user/pictures/* laptop:/home/user/pictures/
Complex transfers
There are many networking technologies that could be used to transfer a large number of files in several directories (CVS, FTP, NFS), but for what we’re doing, most of them don’t make as much sense as rsync.
rsync is a lot like scp, except it’s designed for doing complex transfers. If your laptop and desktop computer share pretty much the same software, /home directory structure, and data, rsync will intelligently update it. If rsync finds duplicate files on the remote machine, it will check to see which file is newer and update the remote file if it is out of date. If the file or directory doesn’t exist, rsync creates it. You can also set rsync to delete any files on the remote machine that are not detected on the local machine, but this can be dangerous, so we won’t use that switch in any examples.
Like with OpenSSH, in order to use rsync for file transfers, you must start a daemon on the remote machine. The command is rsyncd, and you can of course add it to your operating system’s startup script if you like. The examples below do not require the rsync daemon; they use OpenSSH for the file transfers instead, so you’ll have to run the SSH daemon. You can specify which software to use for the file transfers by using either one (SSH) or two (rsync) colons after the remote host name or IP address. The examples that follow all use one colon.
The simplest way to use rsync is to synchronize the /home directories of two computers that have the same users and file system structures:
rsync -arvuz /home/user/ 192.168.1.101:/home/user/
Just like with SSH, you can use /etc/hosts to create a nickname for the IP address of the remote machine, and you can also specify other users with the @ symbol. The -arvuz flags mean (in order) that you’re going to keep user and group file permissions; recursively copy the /home/user/ directory and all files and directories therein; verbosely show which files are transferred or updated; ignore identical files that have the same timestamp; and compress the data to use less bandwidth over the network.
What if you only want to transfer important files and directories — not the whole /home/user/ directory? There are two ways. If you’re copying the same directories in your /home dir each time, you can create a simple script to save yourself all of the typing. Open up your favorite text editor and create a file called sync_laptop.sh and put this into it, substituting your own directories for the ones in the example:
rsync -arvuz '/home/user/pictures /home/user/documents /home/user/jokes' laptop:
When it’s saved, make it executable with chmod +x. The above command won’t work with directories that are more than one level in, so /home/user/pictures/summer/ won’t work. Neither can you copy files to anywhere other than the remote /home directory using the above example. You could add a new line to the script for each buried directory that you want to transfer, but there’s a more efficient way to do it.
First, create a directory in your user’s home dir called sync. Change to the sync directory and create symlinks to all of the directories that you want to transfer. Make sure you treat the sync dir as though it were your home directory when you create the destination links. You may have to create directories within the /home/user/sync/ dir if they are more than one level in:
ln -sf /home/user/documents ./documents
mkdir .gconf
mkdir .gconf/apps
ln -sf /home/user/.gconf/apps/evolution ./.gconf/apps/evolution
ln -sf /home/user/.evolution ./.evolution
mkdir pictures
mkdir pictures/summer
ln -sf /home/user/pictures/summer ./pictures/summer
Now create the script that will do the transfers; call it sync_laptop.sh and put it in your user’s home directory:
# This script syncs a remote computer to this one
cd /home/user/
# Uncomment the next command if you'd like to copy all of
# the files (not directories) in your home dir to the remote machine.
# cp * ./sync/
rsync -arLuvz /home/user/sync/ laptop:/home/user
The -L switch tells rsync to treat your symlinks as though they were real directories.
Save the script, then make it executable by using chmod +x on it. When you run the script, your laptop will be updated with all of those files and directories from your desktop machine. The first time you do this it will take a while, but each subsequent sync will take less time because rsync will not overwrite files that have not changed.
To reverse the process, do the same thing for your laptop computer, but remember to change the IP address or nickname of the remote machine.
By the way — the above example will copy over your email accounts, address books, saved email, and all other Novell Evolution data to the remote machine. If you’re setting up a new laptop computer, this can make moving your Evolution data much simpler.
Further reading
The examples and advice above are simplified for home use. Both OpenSSH and rsync are capable of much more advanced tasks. There are also different techniques and approaches for the processes outlined in our examples. The first and best place to look for more information on these programs is their respective manual pages. If you’re looking for more examples than the ones here and in the man pages, a Google search for the exact switches and options you want to use will turn up more information.
Discuss this article or get technical support on our forum.
Copyright 2006 Jem Matzan.
|
|
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License. |
|
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License.
|
|
Copyright 2008. All content items belong to their respective authors.


