Tuesday, December 9, 2008

Generating A Random Number Within A Range in Java

The task should be simple. If you search for tips, examples, and tutorials on generating random numbers in Java you will find allot of helpful articles. The problem I ran across was that I needed to generate a random number within a range of user specified numbers. Well, this too is simple as you can use the nextInt(n) method of the Random class to accomplish this, right? Well, I must admit, in most cases you can. In my case, however, I couldn't. See, I needed a random number that could potentially be negative as well as positive. So back to searching and you will find that nextInt(n) should also address my need. It should work something like this:


int randNum = random.nextInt( hiNum - loNum + 1 ) + loNum;

Well, this produce the desired result. I can set hiNum and loNum both to positive numbers and get a pretty even distribution of the range. I can also set hiNum and loNum both to negative numbers and get a pretty even distribution of the range. And most importantly I can set loNum to a negative number and hiNum to a positive number and once again get a pretty even distribution of the range. Seems like the perfect solution, right?


Not for me. The problem with this solution is that nextInt(n) accepts an integer as its parameter. An integer seems to work pretty well until we attempt to set the integer to a value larger than Integer.MAX_VALUE. Why would we ever do this you ask? It is simple, the integer value passed to nextInt(n) represents the range of values you want to generate a random number within. The nextInt(n) method was intended to return a positive random integer. What this means is that as soon as we start adding numbers on the negative side of the field to our integer value range we end up with an unsigned integer. In other words the range of -2 to 2 is 5. The range will include -2, -1, 0, 1, and 2. So the nextInt(n) method keeps working until we hit a range of Integer.MAX_VALUE + 1. Once we do that range gets corrupted for all practical purposes because it becomes a negative value which then results in nextInt(n) throwing an exception.


This can be seen by simply setting loNum and hiNum as follows:


int loNum = Integer.MIN_VALUE / 2;
int hiNum = Integer.MAX_VALUE / 2;

Everything would be fine is we add 1 to loNum or subtract 1 from hiNum but as long as we set them as above, expect nextInt(n) to fail.


I searched for quite some time looking for the solution and all solutions that I found made the same mistake in not taking into consideration the maximum size of an integer. The one thing that I did establish very early is that nextInt(n) will not work for my needs as range needs to be a long. So, here is what I came up with:


long range = ( loNum <> 0 ) ? ( ( (long)hiNum - (long)loNum ) + 1) : ( (long)hiNum - (long)loNum );
int randNum = (int)(long)((random.nextDouble() * (long)(range + 1)) + ( ( loNum <> 0 ? (loNum - 1) : loNum )) );

So, I first calculate a long value and set it as the range. If the range is to include the number 0 I have to add 1 to the range to account for it. This is why we see the loNum <> 0 ? ... : .... Once I have calculated my range using a long value I can call the nextDouble() method from the Random class which will return a number between 0.0 and 1.0. I can then take the random double and multiply it by my range. We add 1 to take into account integer rounding that will occur and result in our hiNum not being achieved due to rounding down. I then need to add loNum to the result so that we get the correct range within an integer value. Again, I have to take into account that 0 may or may not be included in the range of integers and if it isn't I need to subtract 1 from the result.


The most important thing I found when putting this together is the types that are involved. What I initially came up with wasn't working because I failed to case loNum and hiNum to long when I was calculating range resulting in range (although a long) being assigned the bad integer value. Once I worked out my casting issues all seemed to come together. The above now allows me to set loNum to Integer.MIN_VALUE and hiNum to Integer.MAX_VALUE and get expected and acceptable results.

Sunday, August 31, 2008

Wrong disk being used when booting from a USB stick with an encrypted root partition

I had decided to play around with installing a full blown Fedora 9 distribution to a 16GB USB flash drive. The installation went well but after the installation was done and the system restarted I found that it would not boot due to a strange issue with the root file system not being found.

It turned out that this was because I enable encryption on theroot file system. The boot loader was not able to mount the encrypted file system for some reason. After further investigation (by removing quiet from the boot command) I found that cryptsetup was attempting to open the encrypted partition on the wrong drive. In my case cryptsetup was attempting to open the encrypted file system from /dev/sdc2 which actually made sense seeing that this partition was /dev/sdc2 at install time but as this is now the boot device it is sda and my encrypted partition is at /dev/sda2.

I began to look through the configuration files in /etc to find the hard coded references to /dev/sdc2 but didn't seem to find anything that would result in this behavior. I then stopped and thought about it for a moment and quickly realized that it couldn't be anything on the root file system causing the conflict as the root file system was not being mounted at boot time. So, I then started picking a part the boot partition to see if I could find this hard coded /dev/sdc2 reference. I did find some references but they did not seem to be the cause of the problem. So, I was back to square one. There had to be something causing this problem but what was it? Out of desperation I decided that the problem may be inside one of the boot images. This thought required me to figure out how to get inside the image and see its content. This didn't take me long
because there seems to be some good references out there about unpacking or mounting a boot image. I started with initrd-<version>.img and quickly saw the actual problem. Inside the initrd image there is a script named init that gets run. It is this script that is the cause of my headaches and lead me tothe process of fixing it.

Here is what I did:

Using the Resuce CD (or another install) mount the boot and encrypted partition on the USB stick. In my case this was /dev/sdc1 (boot) and /dev/sdc2 (LUKS LVM). I used the resuce CD so my encrypted partition was mounted for me (it appeared that I had to enable networking for it to load the LUKS stuff) but the boot partition was not automatically mounted. So for me to mount it I did:
mount /dev/sdc1 /mnt/sysimage/boot 

This mounted the boot partition of my USB stick at /boot of the root file system of my encrypted partion on the USB stick. In other words, the encrypted partition (sdc2) had been mounted by the Rescue CD at /mnt/sysimage.

Changes to the boot partition:

Once mounted I made the simplest change first. I had to modify /mnt/sysimage/boot/grub/device.map and make sure hd0 was set to /dev/sda. Prior to my edit it was set to /dev/sdc which wouldn't be right if the USB stick was the actual boot media.

Changes to the root partition:

Next I fixed the /mnt/sysimage/etc/crypttab file to look for the LUKS partition at /dev/sda2 instead of /dev/sdc2.

Next I fixed the /mnt/sysimage/etc/sysconfig/grub file to look for the boot property at /dev/sda instead of /dev/sdc.

Changes to the initrd image:

Now came the hard part. The reason for the boot failure is because the initrd image (that controls boot up) has been left in charge of mounting the encrypted file system. A startup script that runs prior to any configuration from the actual root file system has a hard-coded setting for the location of the LUKS file system. To fix this you have to modify the initrd image itself.

First you need to extract the initrd image so that you can modify its contents:

mkdir /tmp/initrd-usb
cp /mnt/sysimage/boot/initrd-*.img /tmp/initrd-usb
cd /tmp/initrd-usb
gunzip < initrd-*.img | cpio -id

You should now have the entire contents of the initrd image extracted in /tmp/initrd-usb. We now need to modify the /tmp/initrd-usb/init file and fix the hard coded /dev/sdc2 reference for the encrypted file system. In my case I found three references to sdc3. One of them was the echo statement that printed "Setting up disk encryption: /dev/sdc2" at boot time.

echo Setting up disk encryption: /dev/sdc2 

I changed this one to /dev/sda2 because I wanted the output at boot time to be accurate. The other two references were to the cryptsetup command right below the echo statement:

cryptsetup luksOpen /dev/sdc2 luks-sdc2 

I changed the /dev/sdc2 to /dev/sda2 and luks-sdc2 to luks-sda2. The luks-sda2 will be used as the name of the encrypted device in the device mapper and the /dev/sda2 is the actual partition that contains the encrypted file system.

Once I made the three changes to the init file I had to repackage the initrd image. Before repackaging though I moved the original initrd image file out of /tmp/initrd-usb so that it didn't get included in the new initrd image.

mv /tmp/initrd-usb/initrd-*.img /tmp 

Then I repackaged the initrd image:

cd /tmp/initrd-usb
find ./ | cpio -H newc -o | gzip -9 > /mnt/sysimage/boot/initrd-<kernel version>.img

In my case <kernel version> was 2.6.25-14.fc9.i686 so my command was:

find ./ | cpio -H newc -o | gzip -9  > /mnt/sysimage/boot/initrd-2.6.25-14.fc9.i686.img 

Done!

Once that was done I exited the shell and waited for my machine to reboot. I then booted from the USB stick and sure enough all went as planned and I was up and running.

References:

http://musialek.org/?p=3

This is where I got the specific commands for repackaging the initrd image. Seemed this information wasn't as easy to find as extracting the initrd image contents.

Monday, February 25, 2008

USB Tether AT&T Tilt (WM6) to Linux Laptop (Fedora 8)

I spent sometime over the weekend trying to get my Windows Mobile 6 phone tethered to my laptop so that I could use my phone's Internet access from my laptop. This, in most cases, is a simple process but for some reason it seems a bit more difficult with WM6 on my AT&T Tilt (HTC 8925).

I had done some searching for instructions on how to do this but for the most part, came up empty handed. I should probably mention that I am attempting to tether to a laptop running Fedora 8 (Linux Kernel 2.6.23).

I will say that I found some interesting information that got me pointed in the right direction on the SuSE forums, so I must give credit where credit is due and tell you that http://suseforums.net/index.php?showtopic=41219 is where I found the actual process on how to get the tether to work. I will say that the only relevant thing here is the information on creating the network interface configuration file (ifcfg-rndis0). In my case, this file went in /etc/sysconfig/network-scripts as I am using Fedora. I did not have to get the usb-rndis-lite kernel patches from SVN like the posting indicated I needed to do but this might just be due to the fact that I have the functionality from somewhere/something else. So, if you aren't able to get it to recognize your phone as a network device when you plug in the USB cable, you might want to look at patching the kernel with the usb-rndis-lite package.

Now, here is where I stumbled for some time. In the forum posting referenced above, you are instructed to enable Internet Sharing via USB on the phone. Well, in my case (and from searching, many other cases) the utility that provides Internet Sharing functionality has been replaced by the Wireless Modem utility. From the looks and sounds of it, the Wireless Modem utility does not work. I will admit that I didn't spend too much time trying to get it to work either. I just know that my initial effort proved to go nowhere so I quickly started looking for a way to get the Internet Sharing utility installed and working.

So, I jumped back on the web and starting searching for the utility. What I ran across was many references to installing a CAB from xda-developers that would add the Internet Sharing utility. Although I hate to install applications on my phone (especially when posted on a forum) I decided that I would at least download it and pull it apart to see what it did. Well, that is when I found out that I would have to register for a forum account on xds-developers to continue with a download. Well, this isn't fun and really not fair. So, my next task was to do a search for the CAB file name itself. My thought was that someone would have to had reposted it in some other forum or blog that would not require me to sign-up for an account in order to download it. Well, no luck!

I did not give up though. I remembered that when I first got my phone a few months ago I had stumbled across some Internet or sharing utility of some kind on my phone. I just could not remember what or where I stumbled across it at. So, I began to browse my phone from WM6 File Explorer. After sometime of browsing I came up empty handed. I took a break for an hour or so and then started browsing again. My second browse turned success.

It turns out that Internet Sharing is already installed on my AT&T Tilt (and probably other WM6 devices that are in the same situation). The utility just isn't accessible from the Start menu or Programs/Settings. But this is an easy fix! No need to install or run some custom CAB from some forum posting. Instead, just add the shortcut that already exists on your phone to the Start menu or to Programs. It is that simple!

The shortcut for Internet Sharing is named, wait for it... Internet Sharing. And it is located in the Windows directory. If for some reason you do not have the Internet Sharing shortcut already created on your device in the Windows directory, maybe you can use the IntShrUI shortcut. And if that shortcut doesn't exist, maybe just create your own shortcut referencing the program file name IntShrUI (also located in the Windows directory).

In my case, I simply copied /Windows/Internet Sharing to /Windows/Start Menu/Programs/Tools. Then I was done. I could then start Internet Sharing by going to my Start menu and selecting Programs and then Tools and then Internet Sharing.

Now that I had Internet Sharing up and running on my phone, I could enable it as instructed in the forum posting. Once I did this and created my network interface configuration file (ifcfg- rndis0) I plugged my phone into my laptop via USB and sure enough, I had a new network interface named rndis0 that now had an IP address of 192.168.0.102. Not only did my WM6 phone give my laptop and IP address, it also took care of DNS by giving my laptop a DNS address of 192.168.0.1 (which also happens to be the gateway address).

My next goal is to get this working via Bluetooth. I just like the idea of having my phone on my hip and checking my e-mail from my laptop without the need of pulling on the USB cable or hooking anything up. Of course, if I do get it working via Bluetooth, I would only use such a connection for limited or short sessions. USB is a much better solution as it is a bit more secure and my phone can charge while I am using its Internet access.

Tuesday, January 22, 2008

HowTo Configure AT&T 3G Laplink Connect in RHEL/Fedora Linux

I have forced myself to use Linux and the GNOME desktop for just about everything for the past year and it has gone pretty well. At the start of my Linux usage I had to get an aircard working with RHEL 5. At first, the process seemed difficult but I quickly realized that I lucked out and all worked out-of-the-box.

In my case I was using the AT&T 3G Laplink 875U (aka Sierra Wireless AirCard 875U) and performing the setup in RHEL 5 running kernel 2.6.18-8. I later used the exact same setup in Fedora Core 6 and Fedora 7 without issue.

Furthermore, I used this same setup for an old Verizon aircard that was PCMCIA without any issues. Of course, for the Verizon version the username/password/dialup info was different but the important thing here is the motions.

To configure Cingular/AT&T 3G Laplink Connect (Sierra Wireless AirCard 875U) in RHEL5 from GNOME desktop:
  1. Go to System -> Administration -> Network.
  2. Enter the super-user password.
  3. Go to the Hardware tab.
  4. Click New.
  5. Select Hardware Type of Modem.
  6. Click OK.
  7. Set modem device to /dev/ttyUSB0.
  8. Baud rate should be 460800.
  9. Flow control should be hardware.
  10. Modem volume should be Off.
  11. Use touch tone dialing should be checked.
  12. Click OK.
  13. Click the Device tab.
  14. Click New.
  15. Select Device Type of Modem connection.
  16. Click Forward.
  17. Phone number should be *99#4
  18. Provider name should be Cingular or ATT
  19. Login name should be ISPDA@CINGULARGPRS.COM
  20. Password should be CINGULAR1
  21. Click Forward.
  22. Automatically obtain IP address settings should be checked.
  23. Automatically obtain DNS information form provider should be checked.
  24. Click Forward.
  25. Click Apply.
Using the Network Configuration or Network Device Control tool you should now be able to activate the modem connection and get a connection via the AirCard. If there is a connection failure, the error message shown may not be very descriptive. In such a case, look in syslog for more details.

One problem that I have run into and have not figured out a way around is that the DNS servers obtained during the connection are invalid sometimes. This means that you may not be able to resolve host names to IP addresses. Because of this issue, I have implemented a workaround of setting the DNS address manually. The DNS addresses that I use are 66.209.10.202 and 66.102.163.232. I obtained both of these addresses from Cingular. Please note however, even using this manually set DNS addresses doesn't always work when traveling. It is possible that some network towers will not have access to these DNS servers, in which case using the automatically assigned addresses is better just as long as you get a valid one.

To set these addresses as static:
  1. As superuser, open /etc/sysconfig/network-scripts/ifcfg-Cingular (or ifcfg-ATT depending on the Provider name used during configuration of the connection) using a text editor.
  2. At the end of the file add the following two lines:
    DNS1=66.209.10.202
    DNS2=66.102.163.232
  3. Save the file.
As I use the command-line allot (terminal), I also find it useful to give the new modem connection a name that is easier to type. By default, the device nickname will be Cingular (or ATT) because we gave it a provider name of Cingular. I prefer this to be all lower-case so that I can simply type cingular or att or even better, abbreviated like cing. To get the easier name, change the device nickname to cingular using the Network Configuration tool. I also set some other options for the device to override the default.
  1. Go to System -> Administration -> Network.
  2. Enter the super-user password if prompted.
  3. From the Devices tab, select Device ppp0 / Nickname Cingular / Type Modem. Keep in mind that the device number might be ppp# depending on other modems you have configured.
  4. Click Edit.
  5. Make all the changes you would like. I am not sure how all the settings impact the AirCard as I have not tested them fully but here are the ones that I changed:
    Nickname -> cingular
    Restart if connection dies -> Enabled
If you are using NetworkManager you will need to restart it for it to see the newly added modem. Once restarted, you should can use NetworkManager to establish a connection to Cingular. Due to a bug in NetworkManager, it will not report the status of the connection and therefore, even if connected, NetworkManager will indicate that you are not connected. Also, if you have SELinux set to enforcing, it may prevent pppd from creating the connection because it was invoked by NetworkManager. As I do not use NetworkManager to create the dialup connection, I have not looked into what is required to modify the SELinux policy.

When I do want to connect to Cingular, I use the command line. Open a terminal and issue the following command to connect:
/sbin/ifup cingular

And to shutdown the connection:
/sbin/ifdown cingular

Keep in mind that the argument you pass to ifup or ifdown will be the device nickname given to the dialup connection. In my case this is cingular, but if you did not change the device nickname from Cingular, you would have to use Cingular.