<?xml version="1.0" encoding="ISO-8859-1"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xml:lang="en-US">
	<title>Steve Kamerman&#039;s Blog</title>
	<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php" />
	<modified>2010-03-11T05:38:45Z</modified>
	<author>
		<name>Steve Kamerman</name>
	</author>
	<copyright>Copyright 2010, Steve Kamerman</copyright>
	<generator url="http://www.sourceforge.net/projects/sphpblog" version="0.5.1">SPHPBLOG</generator>
	<entry>
		<title>Tera-WURFL 2.1.0</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry100205-054200" />
		<content type="text/html" mode="escaped"><![CDATA[I&#039;m just about the release Tera-WURFL 2.1.0 (Stable) and I thought I&#039;d give you an idea of what to expect. I was originally positioning this as a revisional update (i.e. 2.0.1), but decided to make it a minor version upgrade because it adds a lot of functionality and includes a few more settings than version 2.0.0. <br /><br /><h3>Here&#039;s what&#039;s new:</h3><br /><br /><b>1. Experimental support for Microsoft SQL Server 2005/2008</b>: with the MSSQL2005 Database Connector, you can now use MS SQL Server as a Tera-WURFL backend! The support is experimental for now, although I&#039;ve put it through my barrage of over 45,000 user agents and it works properly. The Reduction in String stored procedure still needs to be optimized since I just ported it from MySQL 5 to T-SQL. The MSSQL backend is considerably slower than MySQL, and I beleive it&#039;s that procedure slowing it down. <br /><br /><b>2. SimpleDesktop Matching Engine</b>: Tera-WURFL wasn&#039;t originally designed to differnetiate between desktop and mobile browsers (either was WURFL for that matter), but with this release I&#039;ve introduced the SimpleDesktop Matching Engine which, when enabled, uses keywords and regular expressions to detect 90% of desktop browsers without having to resort to searching the database for a matching WURFL entry. In my tests performance increased by 176% for detection of 45,000 actual unique user agents (both mobile and non-mobile). This feature also <b>dramatically</b> decreases the number of items in your cache by using a single cache item for all desktop browsers. <br /><br /><b>3. Capabilities Filter</b>: I&#039;ve been meaning to implement the Capability Filter for a long time, but two very high traffic clients of mine convinced me to sit down and get it finished. This adds a new setting in TeraWurflConfig.php called <b>CAPABILITY_FILTER</b>. If you set it to <b>false</b> it will be disabled and all the capabilities in the WURFL will be stored in the database and available to your scripts (this is the pre-2.1.0 behaviour). Here&#039;s where the magic starts, you can set it to an array of the capabilities and groups of capabilities that you want to store and use, for example, if you just want the know what kind of device is visiting your site and whether or not it&#039;s wireless, you can use this filter: <br /><code><br />public static $CAPABILITY_FILTER = array(<br /> &quot;brand_name&quot;,<br /> &quot;model_name&quot;,<br /> &quot;is_wireless_device&quot;<br />);<br /></code><br />This will shrink your device database by more than a factor of 10. I tested the filter with 20 capabilities against 45,000 unique user agents and it reduced the size of the cache from 645MB (without filtering) to 92MB (with filtering), then down to 24MB with both filtering and SimpleDesktop. <br /><br />You can look forward to the Tera-WURFL 2.1.0 release around February 10, 2010.<br /><br />Tera-WURFL is available at it&#039;s usual location, <a href="http://www.Tera-WURFL.com/" >http://www.Tera-WURFL.com/</a>]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry100205-054200</id>
		<issued>2010-02-05T00:00:00Z</issued>
		<modified>2010-02-05T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Script to update iTunes play count and ratings from any iPod or iPhone</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091222-053304" />
		<content type="text/html" mode="escaped"><![CDATA[I happen to have an iPhone and an iPod Touch sync&#039;d to my iTunes back in the US, but now I&#039;m in Iraq and don&#039;t have access to my computer with the iTunes library on it.  I do have the music, but I don&#039;t want to sync my iPod to my iTunes because all the play counts and ratings will be gone.  I had a little free time last night so I wrote a script to update these attributes for me!  It is written in JScript/JavaScript and uses the iTunes COM Interface to communicate with your device and the iTunes Library.  It is not optimized very well so it will iterate over the entire iTunes Library for each track it encounters on your device, looking for a matching Artist, Album and Track Name to update.  It also maintains a log file that lists every track that was updated including it&#039;s old and new values, as well as any tracks on the iPod/iPhone that it could not find a match for in the iTunes Library.  I created this script for my own purposes and decided to share it, so I don&#039;t intend to provide much support for it.  The zip file contains two files: UpdatePlayCount.bat and UpdatePlayCount.js.  Extract the files somewhere, plug in your iPod/iPhone, then open iTunes and click on your device, then check &quot;Manually manage music and movies&quot; and restart iTunes, then double click on UpdatePlayCount.bat to update your library.  You can see that progress is being made in iTunes by clicking on your Library&#039;s Music folder then sorting the music by Play Count or Rating.  This list is updated in real time.<br /><br /><a href="http://www.teratechnologies.net/stats/phpmyvisites.php?url=http%3A//www.tera-wurfl.com/blog_assets/UpdatePlayCount.zip&amp;id=2&amp;pagename=FILE:UpdatePlayCount%201.0" target="_blank" >Download UpdatePlayCount.zip (2KB)</a><br /><br /><h3>Source Code (JScript/JavaScript)</h3><br /><code><br />/**<br /> * UpdatePlayCount.js<br /> * <br /> * Description<br /> * ---------------------------------------------------------------------------------------------------------<br /> * This script will update your iTunes library with the play counts and ratings from any iPod or iPhone.<br /> * It uses the iTunes COM Interface to communicate with your device.  In order to determine which tracks<br /> * match, the script will search through the iTunes Library looking for a matching Artist, Album and Title;<br /> * as a result, this process can take a long time.  Updates all the music and movies in the &quot;Music&quot; and <br /> * &quot;Movies&quot; playlists on your device.  If you need to update more folders you can modify the code.<br /> *<br /> * Usage<br /> * ---------------------------------------------------------------------------------------------------------<br /> * Double click on UpdatePlayCount.bat<br /> * <br /> * <br /> * @package UpdatePlayCount<br /> * @author Steve Kamerman, stevekamerman AT gmail.com<br /> * @version Alpha 1.0 $Date: 2009/12/22 13:19<br /> * @license <a href="http://www.mozilla.org/MPL/" target="_blank" >http://www.mozilla.org/MPL/</a> MPL Vesion 1.1<br /> * @language Microsoft JScript / JavaScript<br /> *<br /> */<br /><br />var logObject, logFile;<br />var logfileName = &quot;UpdateItunes.log&quot;;<br />logObject = new ActiveXObject(&quot;Scripting.FileSystemObject&quot;);<br />logFile = logObject.CreateTextFile(logfileName, true);<br /><br />var ITTrackKindFile	= 1;<br />var	iTunesApp = WScript.CreateObject(&quot;iTunes.Application&quot;);<br />var sources = iTunesApp.Sources;<br />var i;<br />var updateTracksCount = 0;<br />var missingTracksCount = 0;<br />var ipod;<br />var itunes;<br />var playlists;<br />var itunesPlaylists;<br />var iTunesXML;<br />iTunesXML = &quot;&quot;;<br /><br />for(i=1;i&lt;sources.Count;i++){<br />	if(sources.Item(i).Kind == 2){<br />		ipod = sources.Item(i);<br />		playlists = ipod.Playlists;<br />	}<br />	if(sources.Item(i).Kind == 1){<br />		itunes = sources.Item(i);<br />		itunesPlaylists = itunes.Playlists;<br />	}<br />}<br /><br />if(ipod == undefined){<br />	WScript.Echo(&quot;Error: No iPod or iPhone found.  Please make sure your device is listed in iTunes, then click on it&#039;s name and check \&quot;Manually manage music and videos\&quot;.  Restart iTunes and rerun this script.&quot;);<br />	WScript.Quit(0);<br />}<br />WScript.Echo(&quot;iPod found, press OK to update iTunes with the play counts and ratings from your iPod.&quot;);<br />WScript.Echo(&quot;This process can take a long time to complete.  To monitor the progress,\nopen iTunes and click on your iTunes Music folder\nthen sort by play count or rating and watch them change.\nPress OK to continue&quot;);<br />var currentPlaylist;<br />var currentTracks;<br />var currentTrack;<br />for(i=1;i&lt;playlists.Count;i++){<br />	currentPlaylist = playlists.Item(i);<br />	if(currentPlaylist.Name != &quot;Music&quot; &amp;&amp; currentPlaylist.Name != &quot;Movies&quot;)continue;<br />	currentTracks = currentPlaylist.Tracks;<br />	for(a=1;a&lt;currentTracks.Count;a++){<br />		currentTrack = currentTracks.Item(a);<br />		if(currentTrack.PlayedCount &gt; 0){<br />			updateITunesEntry(currentPlaylist.Name,currentTrack.Artist,currentTrack.Album,currentTrack.Name,currentTrack.PlayedCount,currentTrack.Rating);<br />		}<br />	}<br />}<br /><br />WScript.Echo(&quot;Finished processing &quot;+(updateTracksCount+missingTracksCount)+&quot; tracks.\nUpdated Tracks: &quot;+updateTracksCount+&quot;\nMissing Tracks: &quot;+missingTracksCount+&quot;\nSee the logfile (&quot;+logfileName+&quot;) for more details.&quot;);<br /><br />function updateITunesEntry(playlist, artist, album, song, playCount, rating){<br />	var itunesPlaylist;<br />	var itunesTracks;<br />	var itunesTrack;<br />	var i;<br />	var a;<br />	for(i=1;i&lt;itunesPlaylists.Count;i++){<br />		itunesPlaylist = itunesPlaylists.Item(i);<br />		if(itunesPlaylist.Name != playlist)continue;<br />		itunesTracks = itunesPlaylist.Tracks;<br />		for(a=1;a&lt;itunesTracks.Count;a++){<br />			itunesTrack = itunesTracks.Item(a);<br />			if(itunesTrack.Artist == artist &amp;&amp; itunesTrack.Album == album &amp;&amp; itunesTrack.Name == song){<br />				logFile.WriteLine(itunesTrack.Name+&quot;: count: &quot;+itunesTrack.PlayedCount+&quot;-&gt;&quot;+playCount+&quot;, rating: &quot;+itunesTrack.Rating+&quot;-&gt;&quot;+rating);<br />				itunesTrack.PlayedCount = playCount;<br />				itunesTrack.Rating = rating;<br />				updateTracksCount++;<br />				return(1);<br />			}<br />		}<br />	}<br />	missingTracksCount++;<br />	logFile.WriteLine(&quot;WARNING: Track not found in iTunes Library: &quot;+artist+&quot; - &quot;+song);<br />}<br /></code>]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091222-053304</id>
		<issued>2009-12-22T00:00:00Z</issued>
		<modified>2009-12-22T00:00:00Z</modified>
	</entry>
	<entry>
		<title>I&#039;m finally in Iraq</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091222-053055" />
		<content type="text/html" mode="escaped"><![CDATA[Just wanted to let you know that I&#039;m here in Iraq and beginning my mission.  I may provide more details about where I am and what I&#039;m doing here in the future.  It looks like I won&#039;t have as much time as I thought, but I still plan to get some work done on Tera-WURFL while I&#039;m here!]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091222-053055</id>
		<issued>2009-12-22T00:00:00Z</issued>
		<modified>2009-12-22T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Tera-WURFL vs. New WURFL PHP API</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-225328" />
		<content type="text/html" mode="escaped"><![CDATA[I ran a database of <b>23,902</b> unique user agents through both Tera-WURFL 2.0.0 RC4 and the new WURFL PHP API (1.0.1-rc2).  Here are the results:<br /><br /><b>Tera-WURFL</b><br />Total Time: 12.081017971039<br />Devices Processed: 23902<br />Total Queries: 23912<br /><br /><b>WURFL PHP API</b><br />Total Time: 217.57795381546<br />Devices Processed: 23902<br />Total Queries: N/A<br /><br />Both results were obtained after repeated testing to verify that the user agents were cached.]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-225328</id>
		<issued>2009-10-31T00:00:00Z</issued>
		<modified>2009-10-31T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Tera-WURFL 2 pre-release is available for testing!</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-223856" />
		<content type="text/html" mode="escaped"><![CDATA[Tera-WURFL started in late 2006 as a personal project to make the WURFL PHP Tools faster by storing the device capabilities in a MySQL Database instead of flat files.  It&#039;s now late 2009 and I&#039;ve finally got Tera-WURFL 2.0 up and running.  As of now I&#039;ve released RC4 and I&#039;m working on RC5.  Hopefully I can get version 2.0.0 Stable out before I leave for Iraq.  <b>Here&#039;s a breakdown of the features of Tera-WURFL 2:</b><br /><br />
Rewrote some of the UserAgentMatchers and deleted others to bring Tera-WURFL on par with the Java WURFL API. With the introduction of desktop browser UserAgentMatchers, we no longer need to use the large web patch; instead, you can just use the 8KB one from wurfl.sourceforge.net (included). Also, I fixed some typos and bugs here and there. <strong>NOTE: if you are upgrading from version 2.0.0 RC1-RC3 you should delete all your database tables before you update. You can leave the terawurflcache table if you want to retain your cache.</strong> Although it will still technically work even if you don't delete the tables, you will be orphaning some unecessary tables in your database.</p>
<p>
</strong>Complete code-rewrite from the ground up. The 2.x version of Tera-WURFL is loosely based on a pre-release of the Java WURFL Evolution Library, but the API is taken from Tera-WURFL 1.5.2. The following is a list of features found in Tera-WURFL 2.0:</p>
<ul>
  <li><strong>User Agent Matchers</strong> have been created for each of the major manufacturers. These allow for specific matching methods to be applied to the user agent like string searching, RIS (Reduction in String) and LD (Levenshtein Distance).</li>
  <li><strong>Multiple patch files</strong> are now supported. Tera-WURFL ships with the current <strong>wurfl.xml</strong>, <strong>web_browsers_patch.xml</strong> and <strong>custom_web_patch.xml</strong>. Patch files can be added to <strong>TeraWurflConfig.php</strong> by separating them with semicolons in the <strong>TeraWurflConfig::PATCH_FILE</strong> directive. Patch files are loaded in order from left to right on top of the WURFL file, so if you want to override every other patch file, specify it last.</li>
  <li>The <strong>custom_web_patch.xml</strong> file can be edited from the <strong>Web Administration</strong> page, and allows you to easily add non-mobile user agents to the patch file. The devices with these user agents will be detected as <strong>generic_web_browser</strong> (non-mobile).</li>
  <li><strong>Persistent Caching</strong> means that your cached devices stay cached. When you update the WURFL file or your patches, your device cache is also updated via the new database.</li>
  <li><strong>Cache Browser</strong> allows you to see what devices are hitting your site and what their capabilities were detected as.</li>
  <li><strong>Installation Script</strong> is better than 1.5.2. Once you download Tera-WURFL and extract it, edit <strong>TeraWurflConfig.php</strong> then go to <strong>/admin/install.php</strong> and follow the directions to finish installation.</li>
  <li><strong>PHP short_open_tags</strong> are no longer required to run Tera-WURFL. PHP has this feature disabled by default now.</li>
  <li><strong>Conclusive vs. Inconclusive Matching. </strong>If a device is matched with the UserAgentMatcher's primary matching method it is considered a conclusive match, if it is detected via a recovery matcher or by the CatchAllMatcher it is an inconclusive match. This information is available via the <strong>tera_wurfl</strong> capability group.</li>
  <li><strong>tera_wurfl Capability Group</strong>. The <strong>TeraWurfl->Capabilities</strong> array now contains a group called "tera_wurfl". This group contains the following Tera-WURFL related information:
    <ul>
      <li><strong>num_queries</strong> - the number of database queries required to lookup the device.</li>
      <li><strong>actual_root_device</strong> - the WURFL ID of the actual device (not subrevision or generic), this can be null.</li>
      <li><strong>match_type</strong> - either <strong>conclusive</strong> or <strong>inconclusive</strong>.</li>
      <li><strong>matcher</strong> - the name of the UserAgentMatcher that detected the device.</li>
      <li><strong>match</strong> - whether or not there was an actual match. If there was no match, Tera-WURFL guessed which generic device is most similar to the device.</li>
      <li><strong>lookup_time</strong> - the time in seconds that it took to detect the device.</li>
      <li><strong>fall_back_tree</strong> - the complete fallback tree that built the capabilities of the device. This is a list of all the WURFL IDs from the detected device down to the base generic device.</li>
    </ul>
  </li>
  </ul><br /><br /><h3>Example Script</h3><br /><code><br />&lt;?php<br />// Include the Tera-WURFL file<br />require_once(&quot;TeraWurfl.php&quot;);<br />// Instantiate the Tera-WURFL object<br />$wurflObj = new TeraWurfl();<br />// Get the capabilities from the object<br />$matched = $wurflObj-&gt;GetDeviceCapabilitiesFromAgent(); //optionally pass the UA and HTTP_ACCEPT here<br />// Show whether there was a conclusive match<br />if($matched){echo &quot;Match found&quot;;}else{echo &quot;Match NOT found&quot;;}<br />// Print the capabilities array<br />echo &quot;&lt;pre&gt;&quot;.htmlspecialchars(var_export($wurflObj-&gt;capabilities,true)).&quot;&lt;/pre&gt;&quot;;<br />?&gt;<br /></code>]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-223856</id>
		<issued>2009-10-31T00:00:00Z</issued>
		<modified>2009-10-31T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Back to Iraq!</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-215958" />
		<content type="text/html" mode="escaped"><![CDATA[<img src="images/steve.jpg" width="452" height="549" border="0" alt="" /><br /><h3>I&#039;m heading back to the desert for another deployment with the Army National Guard</h3><br />I&#039;ve been in the Army National Guard since 2003 and have been lucky enough to serve only one tour in Iraq (2005).  In 2008 I moved from Michigan to Texas and in doing so I was forced to switch Army units.  The unit that grabbed me up was already deployed and I was happily holding down the fort in the rear detachment.  Unfortunately, a quick 3 months later I was scooped up by a different unit 300 miles away for a deployment.  I am now in the final stages of my preparation for this deployment and am scrambling to wrap up all my stuff before I leave.  Hopefully I will have a lot of time to work on Tera-WURFL when I&#039;m gone; I plan to further abstract the database layer and introduce a MSSQL Database Connector in the coming months.  <b>I will let you know once I&#039;m in the sandbox!</b><br />]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry091030-215958</id>
		<issued>2009-10-31T00:00:00Z</issued>
		<modified>2009-10-31T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Recovering a RAID 0 array with Linux and Python!</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry090528-010324" />
		<content type="text/html" mode="escaped"><![CDATA[<h4>Background of my problem</h4><br />A few days ago my Dell XPS 720 decided to die on me.  When I push the power button it turns on for about 1/2 second and then turns off.  After much debate, I bought a new power supply for it (non-standard of course [24pin AND 20pin power connectors]).  Today I received the power supply and low-and-behold that wasn&#039;t the problem.  Now I need to buy either a motherboard or a new CPU (the existing one is a Core 2 Quad Extreme 3.0).  I decided that buying from Dell was a bad choice and I&#039;ll just build myself a Phenom II system.  Meanwhile, the XPS 720 shipped with 2x 120GB 10k Raptors in a RAID-0 using the sub-par onboard SATA RAID controller.  I would just love to get my data off the system, but I can&#039;t get it up and running and I&#039;m not going to sink anymore money into it.  I decided to figure out exactly how the data was stored on the array members and try to deinterlace it directly into a raw image that I could then mount via loopback device in Linux and copy all my data back out :)  It sounds easy, huh?  I found <a href="http://indianalinux.blogspot.com/2008/05/howto-reconstruct-failed-raid-0-arrays.html" target="_blank" >this GREAT little python code snippet</a> that does the heavy lifting for me - thanks Simón A. Ruiz!!!<br /><br /><h4>How I recovered the array</h4><br />First, I noted which SATA ports my drives were plugged into on my XPS and labeled them SATA0 and SATA1 so I would remember (if you already unplugged them, fdisk -l the drives, the one with a valid partition table is SATA0).  Once they were labeled, I went to another desktop and popped in an ubuntu 8.10 x64 Desktop CD that I had laying around and booted into the GUI (just so I could get a bigger terminal ;) ).  Once in the GUI, I loaded up a couple of terminals and got root with <b>sudo su -</b>.  Now I used fdisk to find my drives:<br /><br /><code><br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -l /dev/sda<br /><br />Disk /dev/sda: 203.9 GB, 203928109056 bytes<br />255 heads, 63 sectors/track, 24792 cylinders<br />Units = cylinders of 16065 * 512 = 8225280 bytes<br />Disk identifier: 0x2b242b24<br /><br />   Device Boot      Start         End      Blocks   Id  System<br />/dev/sda1   *           1       18935   152095356    7  HPFS/NTFS<br />/dev/sda2           18936       24036    40973782+  83  Linux<br />/dev/sda3           24037       24792     6072570    5  Extended<br />/dev/sda5           24037       24792     6072538+  82  Linux swap / Solaris<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -l /dev/sdb<br /><br />Disk /dev/sdb: 160.0 GB, 160000000000 bytes<br />255 heads, 63 sectors/track, 19452 cylinders<br />Units = cylinders of 16065 * 512 = 8225280 bytes<br />Disk identifier: 0x20000000<br /><br />   Device Boot      Start         End      Blocks   Id  System<br />/dev/sdb1               1           6       48163+  de  Dell Utility<br />/dev/sdb2               7        1312    10485760    7  HPFS/NTFS<br />/dev/sdb3   *        1312       38905   301963264    7  HPFS/NTFS<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -l /dev/sdc<br /><br />Disk /dev/sdc: 160.0 GB, 160000000000 bytes<br />255 heads, 63 sectors/track, 19452 cylinders<br />Units = cylinders of 16065 * 512 = 8225280 bytes<br />Disk identifier: 0x00000000<br /><br />Disk /dev/sdc doesn&#039;t contain a valid partition table<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -l /dev/sdd<br /><br />Disk /dev/sdd: 750.1 GB, 750156374016 bytes<br />255 heads, 63 sectors/track, 91201 cylinders<br />Units = cylinders of 16065 * 512 = 8225280 bytes<br />Disk identifier: 0x8d399bc0<br /><br />   Device Boot      Start         End      Blocks   Id  System<br />/dev/sdd1               1       91201   732572001    7  HPFS/NTFS<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -l /dev/sde<br /><br />Disk /dev/sde: 16.0 GB, 16013852672 bytes<br />78 heads, 14 sectors/track, 28641 cylinders<br />Units = cylinders of 1092 * 512 = 559104 bytes<br />Disk identifier: 0x2c26e771<br /><br />   Device Boot      Start         End      Blocks   Id  System<br />/dev/sde1               8       28642    15634496    c  W95 FAT32 (LBA)<br /></code><br /><br />In my case, <b>sda</b> is my recovery computer&#039;s hard drive, <b>sdb</b> is my SATA0 array member, <b>sdc</b> is my SATA1 array member, <b>sdd</b> is my external firewire drive (I&#039;ll use this to store the data) and <b>sde</b> is my thumbdrive.<br /><br />Note how /dev/sdb has a valid partition table and /dev/sdc does not - this is because of how RAID-0 works, it uses <b>striping</b> to put stripes (pieces) of data on each drive, alternating back and forth - the first drive in a RAID-0 should always have the partition table on it since that data is in the first stripe.  The stripe size can vary depending on your setup and RAID controller - mine is 64k, so the RAID controller writes 64K of data to SATA0, then the next 64K to SATA1 and so forth.<br /><br />Next I took the python script from Simón A. Ruiz&#039; blog and modified it to show me a status indication:<br /><br /><code><br />#!/usr/bin/python<br />#<br /># raid0_deinterlace.py<br />#<br /># INTENT = This is a script for deinterlacing two raw dd images<br />#	 or drives from a RAID-0 and combine them into a single<br />#	 image file<br />#<br />#		  This is strictly experimental.<br />#<br />#	Original Script By: Simon A. Ruiz; Thursday, May 1, 2008<br />#<br />#	Modified By: Steve Kamerman; Thursday, May 28, 2009<br />#		Changes: Added status indicator so you can estimate ETA<br /><br />import datetime<br /><br />inputFiles = [open(&quot;/dev/sdb&quot;,&quot;rb&quot;),open(&quot;/dev/sdc&quot;,&quot;rb&quot;)]<br />outputFile = open(&quot;output&quot;,&quot;wb&quot;)<br />chunkSize = 65536  # change this to your stripe size in bytes<br /><br /># And, so as not to have to figure this out every time through the loop...<br />numFiles = len(inputFiles)<br /><br />i = 0<br />a = 0<br />gb = 0<br />while True:<br />	if a == 16384:<br />		gb += 1<br />		print &#039;Copied&#039;, gb, &#039;GB of data&#039;, datetime.datetime.now()<br />		a = 0<br />	nextChunk = inputFiles[i%numFiles].read(chunkSize)<br />	if not nextChunk:<br />		print &#039;Done! No more data.&#039;<br />		break<br />	outputFile.write(nextChunk)<br />	i += 1<br />	a += 1<br /><br />outputFile.close()<br />for file in inputFiles:<br />	file.close()<br /></code><br /><br />Since I&#039;m using /dev/sdb and /dev/sdc, this script is setup correctly for me.  If you are using different devices, change those names in script.  You can also point the script at dd images instead of physical drives if that&#039;s what you&#039;re trying to recover from.  This process will create a single file that contains the entire array in raw image form with the name <b>output</b> in the current working directory.<br /><br />When you run this script you will see a notification for every GB it processes, something like this:<br /><br /><code><br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# ./raid0_deinterlace.py <br />Copied 1 GB of data 2009-05-27 23:14:30.742032<br />Copied 2 GB of data 2009-05-27 23:15:01.828132<br />Copied 3 GB of data 2009-05-27 23:15:33.043268<br />Copied 4 GB of data 2009-05-27 23:16:04.179799<br />Copied 5 GB of data 2009-05-27 23:16:35.243319<br />Copied 6 GB of data 2009-05-27 23:17:06.471911<br />Copied 7 GB of data 2009-05-27 23:17:37.526823<br />Copied 8 GB of data 2009-05-27 23:18:09.352929<br />Copied 9 GB of data 2009-05-27 23:18:40.196855<br />Copied 10 GB of data 2009-05-27 23:19:11.309716<br />Copied 11 GB of data 2009-05-27 23:19:42.529761<br />Copied 12 GB of data 2009-05-27 23:20:13.553971<br />Copied 13 GB of data 2009-05-27 23:20:44.419778<br />Copied 14 GB of data 2009-05-27 23:21:15.461924<br />Copied 15 GB of data 2009-05-27 23:21:46.650109<br />Copied 16 GB of data 2009-05-27 23:22:17.699218<br />Copied 17 GB of data 2009-05-27 23:22:48.770149<br />Copied 18 GB of data 2009-05-27 23:23:19.780599<br />Copied 19 GB of data 2009-05-27 23:23:51.019963<br />Copied 20 GB of data 2009-05-27 23:24:22.066913<br />Copied 21 GB of data 2009-05-27 23:24:59.482170<br />Copied 22 GB of data 2009-05-27 23:25:33.433475<br />Copied 23 GB of data 2009-05-27 23:26:04.662601<br />Copied 24 GB of data 2009-05-27 23:26:36.083603<br />Copied 25 GB of data 2009-05-27 23:27:09.586955<br />Copied 26 GB of data 2009-05-27 23:27:39.184255<br />Copied 27 GB of data 2009-05-27 23:28:19.708569<br />Copied 28 GB of data 2009-05-27 23:28:51.849345<br />Copied 29 GB of data 2009-05-27 23:29:23.161374<br />Copied 30 GB of data 2009-05-27 23:29:54.155994<br />Copied 31 GB of data 2009-05-27 23:30:25.264944<br />Copied 32 GB of data 2009-05-27 23:30:56.101558<br /></code><br /><br /><b>Here&#039;s how to verify that this process is going to work:</b><br />Wait for a couple GBs to finish copying, then you can fdisk the new image and even mount it if the entire partition has already been copied!  Here&#039;s how to check the partition table:<br /><br /><code><br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# fdisk -lu output <br />You must set cylinders.<br />You can do this from the extra functions menu.<br /><br />Disk output: 0 MB, 0 bytes<br />255 heads, 63 sectors/track, 0 cylinders, total 0 sectors<br />Units = sectors of 1 * 512 = 512 bytes<br />Disk identifier: 0x20000000<br /><br /> Device Boot      Start         End      Blocks   Id  System<br />output1              63       96389       48163+  de  Dell Utility<br />output2           98304    21069823    10485760    7  HPFS/NTFS<br />Partition 2 has different physical/logical endings:<br />     phys=(1023, 254, 63) logical=(1311, 136, 41)<br />output3   *    21069824   624996351   301963264    7  HPFS/NTFS<br />Partition 3 has different physical/logical beginnings (non-Linux?):<br />     phys=(1023, 254, 63) logical=(1311, 136, 42)<br />Partition 3 has different physical/logical endings:<br />     phys=(1023, 254, 63) logical=(38904, 57, 1)<br /><br /></code><br /><br />Don&#039;t worry about the errors for now - fdisk isn&#039;t happy that the file isn&#039;t written completely yet.  Meanwhile, I was able to mount my two first partitions since they we very small.  Note the starting sector of the partition you want to mount by looking at the fdisk -lu output (the -u displays sectors so it is mandatory).  For my first partition, the starting sector is 63 (very very common).  Now we are ready to mount the image.  The mount command loop option takes a starting offset in bytes, but we see it as a starting sector (63 for me).  Multiplying the sectors by 512 gets us the byte offset that mount is looking for.  I put this multiplication in the code, so you just need to replace the 63 with your starting sector.<br /><br /><code><br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# mkdir /media/dell<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# mount -o loop,ro,offset=$((63*512)) -t auto output /media/dell<br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# ls -al /media/dell/<br />total 168<br />drwxr-xr-x 3 root root 16384 1969-12-31 18:00 .<br />drwxr-xr-x 6 root root   160 2009-05-27 23:57 ..<br />-rwxr-xr-x 1 root root   726 2007-10-19 12:51 autoexec.bat<br />-rwxr-xr-x 1 root root   726 2007-08-13 10:45 autoexec.up<br />-r-xr-xr-x 1 root root 53569 2005-06-21 11:54 command.com<br />-rwxr-xr-x 1 root root   137 2007-10-19 12:51 config.sys<br />-rwxr-xr-x 1 root root   137 2007-08-13 10:45 config.up<br />-rwxr-xr-x 1 root root    85 2007-08-13 10:45 copyup.bat<br />-r-xr-xr-x 1 root root 29690 2005-07-25 11:48 dellbio.bin<br />-rwxr-xr-x 1 root root   320 2007-08-13 10:45 delldiag.ini<br />-r-xr-xr-x 1 root root 33352 2005-06-21 11:54 dellrmk.bin<br />drwxr-xr-x 2 root root  4096 2007-10-19 12:51 diags<br />-rwxr-xr-x 1 root root 15299 2007-08-13 10:46 himem.sys<br />-rwxr-xr-x 1 root root     7 2007-10-29 03:12 oobedone.flg<br /></code><br /><br />As you can see, I am able to access the data on that partition just fine!  Using this same method, I can access my big partition:<br /><code><br />root@ubuntu:/media/750GB EXT/RAID_RECONSTRUCT# mount -o loop,ro,offset=$((21069824*512)) -t auto output /media/xps_c_drive<br /></code><br /><br />I&#039;m getting really tired now and my screwdrivers are starting to kick in, so I&#039;m going to call this one finished!<br /><br /><b>-------------------------------<br />     UPDATE July 27, 2009<br />-------------------------------</b><br /><br />I finally have the time and space to recover my data, so I sat down and did it.<br /><br />Here&#039;s what I&#039;ve got mounted:<br /><br /><code><br />/dev/sdb1 on /tmp/750gb type fuseblk (rw,nosuid,nodev,allow_other,blksize=4096)<br />/dev/sdc1 on /tmp/1tb type vfat (rw)<br />/dev/loop1 on /tmp/RAID type fuseblk (ro,nosuid,nodev,allow_other,blksize=4096)<br /></code><br /><br />I tried a number of ways to copy the data, but it kept failing because I was copying from an NTFS image to a FAT32 drive - their were files over the 4GB-1Byte limit, filenames over the limit and directories nested too deeply.  I don&#039;t really care about these files anyway.  A quick search revealed that the offending large files were just junk anyway (and a game I don&#039;t play):<br /><br /><code><br />find /tmp/RAID -size +4G -print<br /></code><br /><br />Then I used rsync to copy the data from the loop-mounted array to the new storage location:<br /><br /><code><br />ubuntu@ubuntu:/tmp/RAID$ sudo rsync --size-only --max-size=4g-1 -rtv /tmp/RAID/ /tmp/1tb/RAID_DATA/ 2&gt;&amp;1 &gt; /tmp/1tb/RAID_DATA.log<br /></code><br /><br />The <b>--max-size=4g-1</b> directive tells rsync to skip files that are larger than FAT32 supports (4GB minus 1 Byte).  This is ONLY necessary when copying to FAT32.  The rest of the options are easy to understand from the rsync MAN page.<br /><br />Horray!  I&#039;ve got my data back!]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry090528-010324</id>
		<issued>2009-05-28T00:00:00Z</issued>
		<modified>2009-05-28T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Linux LVM Snapshot Backups</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080803-221718" />
		<content type="text/html" mode="escaped"><![CDATA[I&#039;ve used UFS2 snapshots on FreeBSD by means of the <b>dump</b> command to make stateful filesystem backups in the past, so I recently wanted to take a similar approach to backup some Linux servers.  Most GNU/Linux distributions use ext3 as their root filesystem by default these days which does not natively support snapshots, so I needed to find another way to do it.  As it turns out, I used LVM on my main Linux servers to add a layer of virtualization to the storage subsystem.  LVM does natively support snapshots, and here&#039;s how to do it.<br /><br /><h3>1. Make room for the snapshot</h3> <br />I ran into a major problem right off the bat - the server I was testing LVM snapshots on had one hard drive with 2 partitions, 1 for /boot and one for LVM.  The LVM partition contained one Volume Group called &quot;TERA-VIRTUAL&quot; and this VG had 2 Logical Volumes - &quot;root&quot; (mounted on /) and swap_1 (swap). The root LV had all the PE allocated to it and the filesystem beneath it was using all of the available space.  The problem is that since there were no available Physical Extents in the VG (or in my PVs), I couldn&#039;t create a snapshot volume.  This is important: <b>when you create a snapshot on LVM you are really creating another Logical Volume that is used to hold the changes to the volume.  This volume needs to me in the same Volume Group as the volume you are taking a snapshot of - so you need some extra space in that VG!</b>  In my case, I needed to resize the root fs, which was on a Logical Volume - this to me an hour plus to achieve since most LiveCDs either have no support for LVM (gparted-live) or the support is broken (backtrack 3 final usb).  I booted ubuntu server 7.10 64bit into rescue mode and started a shell in the installer environment and once at a prompt typed <b>vgmknodes</b> to make the /dev/ entries for my LVs, then used <b>resize2fs</b> to shrink my FS and <b>lvreduce</b> to shrink my LV, leaving me with 23G of available Physical Extents in my VG (seriously, if you don&#039;t know what I&#039;m talking about, don&#039;t try it - you <b>will</b> trash your hard drive!).<br /><br /><h3>2. Create the snapshot and run backup</h3><br />To create a snapshot you first need to figure out how much space you need - <b>it is critical that you have enough space to hold all the changes to your volume until the snapshot is removed!</b>.  For me 10% of my 200G is fine - 20G.<br />The process is to create a snapshot LV, mount it, do your backup, unmount it, and remove the snapshot:<br /><br /><code><br />root@TERA-VIRTUAL:~# lvcreate --size 20G --snapshot --name snap /dev/TERA-VIRTUAL/root<br />  Logical volume &quot;snap&quot; created<br />root@TERA-VIRTUAL:~# lvs<br />  LV     VG           Attr   LSize   Origin Snap%  Move Log Copy%<br />  root   TERA-VIRTUAL owi-ao 200.00G<br />  snap   TERA-VIRTUAL swi-a-  20.00G root     0.00<br />  swap_1 TERA-VIRTUAL -wi-ao   9.46G<br />root@TERA-VIRTUAL:~# lvdisplay /dev/TERA-VIRTUAL/snap<br />  --- Logical volume ---<br />  LV Name                /dev/TERA-VIRTUAL/snap<br />  VG Name                TERA-VIRTUAL<br />  LV UUID                fFbaH4-22Hq-s7a2-e1mo-DPov-8wff-dvuAiI<br />  LV Write Access        read/write<br />  LV snapshot status     active destination for /dev/TERA-VIRTUAL/root<br />  LV Status              available<br />  # open                 0<br />  LV Size                200.00 GB<br />  Current LE             51200<br />  COW-table size         20.00 GB<br />  COW-table LE           5120<br />  Allocated to snapshot  0.01%<br />  Snapshot chunk size    8.00 KB<br />  Segments               1<br />  Allocation             inherit<br />  Read ahead sectors     0<br />  Block device           254:2<br /><br />root@TERA-VIRTUAL:~# mount /dev/TERA-VIRTUAL/snap /backup_snapshot/<br />root@TERA-VIRTUAL:~# cp -Rf /backup_snapshot/VirtualMachines/TERA-WEBSERVER /backup/<br />root@TERA-VIRTUAL:~# lvdisplay /dev/TERA-VIRTUAL/snap<br />  --- Logical volume ---<br />  LV Name                /dev/TERA-VIRTUAL/snap<br />  VG Name                TERA-VIRTUAL<br />  LV UUID                fFbaH4-22Hq-s7a2-e1mo-DPov-8wff-dvuAiI<br />  LV Write Access        read/write<br />  LV snapshot status     active destination for /dev/TERA-VIRTUAL/root<br />  LV Status              available<br />  # open                 1<br />  LV Size                200.00 GB<br />  Current LE             51200<br />  COW-table size         20.00 GB<br />  COW-table LE           5120<br />  Allocated to snapshot  1.96%<br />  Snapshot chunk size    8.00 KB<br />  Segments               1<br />  Allocation             inherit<br />  Read ahead sectors     0<br />  Block device           254:2<br /><br />root@TERA-VIRTUAL:~# umount /backup_snapshot/<br />root@TERA-VIRTUAL:~# lvremove /dev/TERA-VIRTUAL/snap<br />Do you really want to remove active logical volume &quot;snap&quot;? [y/n]: y<br />  Logical volume &quot;snap&quot; successfully removed<br /></code><br /><br />As you can see from the transcript above, you can keep an eye on your snapshot size by using <b>lvdisplay</b> and looking for &quot;Allocated to snapshot&quot;.  As seen above, before the backup the snapshot volume was 0.01% full, but by the time my backup finished, it was up to 1.96%.<br /><br />Now I&#039;ve got a sane copy of my data without having to dismount my filesystem!<br />]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080803-221718</id>
		<issued>2008-08-04T00:00:00Z</issued>
		<modified>2008-08-04T00:00:00Z</modified>
	</entry>
	<entry>
		<title>FreeBSD watch command is not GNU/Linux watch command</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080710-203456" />
		<content type="text/html" mode="escaped"><![CDATA[Having done Linux administration for over 10 years now, I use the watch command almost every day.  On Linux, watch will run a command every couple seconds.  It&#039;s particularly nice for watching files change:<br /><br /><code><br /># watch ls -lah<br /></code><br /><br />This example will keep showing the output of ls -lah every 2 seconds.<br /><br />About a year ago I switched a couple servers over to FreeBSD 6.1 because of it&#039;s security features and more stable development model.  For the last year I&#039;ve been occasionally using the watch command on FreeBSD and getting an annoying error like this:<br /><br /><code><br />[root@devel /backup]# watch ls -lah<br />watch: fatal: bad device name<br /></code><br /><br />Today it finally occured to me to look at the manpage:<br /><br /><code><br />WATCH(8)                FreeBSD System Manager&#039;s Manual               WATCH(8)<br /><br />NAME<br />     watch -- snoop on another tty line<br /><br />SYNOPSIS<br />     watch  -cinotW -f snpdev tty<br /><br />DESCRIPTION<br />     The watch utility allows the user to examine all data coming through a<br />     specified tty using the snp(4) device.  If the snp(4) device is not<br />     available, watch will attempt to load the module (snp).  The watch util-<br />     ity writes to standard output.<br /></code><br /><br />It hardly looks like the watch I know!  It turns out that FreeBSD has a different use for watch - snooping on other consoles.  On FreeBSD, the GNU watch (like on Linux) is called <b>gnu-watch</b>.  If you don&#039;t want to install it, you can easily whip up a poor-man&#039;s replacement:<br /><br /><code><br />#!/usr/local/bin/bash<br />while [ 1 -lt 2 ]<br />do<br />clear<br />date<br />echo ------------------------------------<br />eval $@<br />sleep 2<br />done<br /></code><br /><br />TIP: To replace the default &#039;watch&#039; with this script, save the above script as <b>/root/watch</b>, then make it executable, <b>chmod +x /root/watch</b> and alias it in your shell, <b>alias watch=&#039;/root/watch&#039;</b>.  You can save this alias permenantly with the bash shell by adding it to ~/.bash_profile .<br /><br />Here&#039;s a sample output from the poor-man&#039;s watch:<br /><code><br />Thu Jul 10 22:09:27 EDT 2008<br />------------------------------------<br />total 17113832<br />drwxr-xr-x   3 root  wheel   512B Jul 10 21:03 .<br />drwxr-xr-x  21 root  wheel   512B Jul 10 18:25 ..<br />-rw-r--r--   1 root  wheel    97M Jul 10 15:41 archive1.tar<br />-rw-r--r--   1 root  wheel    40M Jul 10 15:41 archive1.tar.bz2<br />-rw-r--r--   1 root  wheel   308M Jul 10 17:19 archive2.tar<br />-rw-r--r--   1 root  wheel    93M Jul 10 17:19 archive2.tar.bz2<br />-rw-r--r--   1 root  wheel   600M Jul 10 17:13 archive2a.tar<br />-rw-r--r--   1 root  wheel   192M Jul 10 17:13 archive2a.tar.bz2<br />-rw-r--r--   1 root  wheel   3.3G Jul 10 19:09 archive2b.tar<br />-rw-r--r--   1 root  wheel   1.4G Jul 10 19:09 archive2b.tar.bz2<br />-rw-r--r--   1 root  wheel   5.4G Jul 10 19:33 archive2c.tar<br />-rw-------   1 root  wheel   1.9G Jul 10 22:09 archive2c.tar.bz2<br />-rw-r--r--   1 root  wheel   2.1G Jul 10 17:44 archive2d.tar<br />-rw-r--r--   1 root  wheel   886M Jul 10 16:08 archive3.tar<br />drwxr-xr-x   2 root  wheel   512B Jul 10 19:44 scripts<br /></code>]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080710-203456</id>
		<issued>2008-07-11T00:00:00Z</issued>
		<modified>2008-07-11T00:00:00Z</modified>
	</entry>
	<entry>
		<title>Dreamweaver crashes every day</title>
		<link rel="alternate" type="text/html" href="http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080618-112602" />
		<content type="text/html" mode="escaped"><![CDATA[I use Adobe Creative Suite 3 (CS3) Web Premium every day, Flash, Fireworks, Photoshop, but most of all Dreamweaver.  I consider Dreamweaver CS3 to be the best HTML/CSS visual editor available and a it&#039;s a very handy FTP client as well.  It also serves as a nice text editor with syntax highlighting for PHP (although PDT for Eclipse is MUCH better).  My problem is that it crashes on me almost every day with this really annoying error: <b>Runtime Error!  This application has requested the Runtime to terminate it in an unusual way.</b>  Here&#039;s what it looks like:<br /><br /><img src="images/dreamweaver_crash.jpg" width="476" height="257" border="0" alt="" /> <br /><br />This error makes my blood boil!  I&#039;ve done everything to try it including, but not limited to:<br /><br /><h3>Delete the configuration Files</h3><br />This seems to give me the most success.  Dreamweaver stores a file cache and some non-site related settings in <b>C:\Users\[your username]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration</b>.  If you delete or rename this directory then Dreamweaver will recreate it when it starts up.<br /><br /><h3>Delete the Dreamweaver Windows Prefetch file</h3><br />When a program starts up, Windows trys to optimize the startup process by creating a &quot;cache&quot; type file in <b>C:\windows\Prefetch</b>.  Next time the program opens it uses this file to speed up the process.  You can safely delete everything in the Prefetch folder.  Doing this helped me get Dreamweaver working yesterday.<br /><br /><h3>Get rid of Logitech SetPoint</h3><br />Someone on an Adobe forum said this worked for them - I haven&#039;t had much success<br /><br /><h3>Restart computer with only essential processes enabled</h3><br />Also from Adobe, this seems to have no impact on my computer - it still crashes.<br /><br /><h3>Maybe it&#039;s the 8KB file bug???</h3><br />According to many people on Adobe&#039;s forum, if you a working on a file that is exactly 8,192 bytes (8KB) Dreamweaver will crash.  Well, my files are taking up 8,192K because the block size on my disk pushes 7+K files up to 8,192 (look at &quot;size on disk&quot;), but I put a huge HTML comment at the end of the file to push it past 10K and it didn&#039;t help.<br /><br /><h3>Doing the exact same thing every time will make Dreamweaver crash every time</h3><br />Here&#039;s my most effective solution as of this morning: once Dreamweaver crashes, you can&#039;t do the exact same thing when you reopen it or it will just keep crashing.  I was trying to add a 4x3 table to a document and everytime I did Insert-&gt;Table-&gt;OK it immediately crashed (I did this 10+ times while changing other Windows stuff).  Then I went to show my business partner and instead of adding a table, I went to a different Site and changed some code around, then when back to the original site and BAM!  it works fine now!  What the <b>HECK</b> is going on here?<br /><br /><h3>Final thoughts</h3><br />You know, I&#039;ve been working with Linux and open source software for over 10 years, and I finally break down and by a $2,000+ software package - I expect it to work!  If it doesn&#039;t work I expect that I can get support to make it work (and not that &quot;please restart your computer then uninstall/reinstall&quot; BS)!  Come on Adobe!!!  If I charge my clients &gt;$100/hr and I have to fight with Dreamweaver for 1 hr per day, where&#039;s my $36,500 from Adobe for the last year?<br /><br />P.S. I think the problem is related to MS Visual Studio 2005&#039;s runtime update and Vista or something.]]></content>
		<id>http://www.teratechnologies.net/stevekamerman/index.php?entry=entry080618-112602</id>
		<issued>2008-06-18T00:00:00Z</issued>
		<modified>2008-06-18T00:00:00Z</modified>
	</entry>
</feed>
