Showing posts with label Computers. Show all posts
Showing posts with label Computers. Show all posts

Tuesday, August 22, 2017

Fixing the annoying popup in FreeIPA

I love FreeIPA except for one thing: That annoying popup when using Chrome:
















This popup confuses the hell out of my users when they can't log in. So what is it? One nice feature of FreeIPA is it's ability to do Single Sign on via Kerberos. The problem is that not all browsers support this and the configuration of this feature for users has a high bar of entry - I'm not even sure you can get it working in Chrome. So when a user goes to the FreeIPA page which supports Kerberos logins and sees this popup they (understandably) think they need to log in. Since the browser is not configured for Kerberos, this inevitably fails. But what is worse, is that unauthenticated users are prompted to log in twice. So pretty quickly this can turn into a service call or ticket when a user "can't login." The workaround is to click cancel twice to be taken to the forms based login. But instead, why don't we just suppress this popup?

Since the FreeIPA documentation is notoriously abysmal, I'll provide my solution to the problem here. Simply do the following:

Log in to your host
edit /etc/httpd/conf.d/ipa-rewrite.conf
Add to the end of this file:


#The following disables the annoying kerberos popup for Chrome
RewriteCond %{HTTP_COOKIE} !ipa_session
RewriteCond %{HTTP_REFERER} ^(.+)/ipa/ui/$
RewriteRule ^/ipa/session/json$ - [R=401,L]
RedirectMatch 401 ^/ipa/session/login_kerberos


And that's it! Restart your web server and this popup should go away! So what does it do?

The first three lines set very specific conditions for triggering a 401 response for the page /ipa/session/json. The web client uses JSON to communicate with the IPA server, so we only want to send the 401 response if there is no ipa_session cookie (set on login) and the user is coming from the ui page and not, say, making their own JSON call from the command line. Otherwise we risk inadvertently breaking something.

It is worth noting that in older versions of FreeIPA, this path was /ipa/session/xml, but it was renamed. If you are on an old version of IPA either upgrade or adjust your configuration appropriately.

The second thing this does is returns a 401 when someone tries to access the kerberos login page. It is these two pages that generate the http basic authentication login and by immediately returning a 401 instead of allowing these pages to try to authenticate with kerberos, we are ultimately able to resolve this issue.

Wednesday, December 28, 2016

11 questions IT people should ask in an interview

Job seekers are frequently advised that an interview is not just for prospective employers to see if you, the interviewee are the right fit, but also for the interviewee to see if they find the prospective employer to be a right fit. Every prospective employer should give the interviewee the opportunity to ask any questions they might have. You should be leery of any interview in which you are not given sufficient time to ask questions. Clearly, prospective employers who do not make time for you now certainly aren't going to be more considerate after you are hired. In fact, companies will never treat you as well as when they are looking to hire you. Generally, the better you are treated during an interview, the better you will be treated during your employment. As such, be sure to leverage the opportunity during your interview to get answers to the following 11 questions:

  1. What is your background? Were you promoted from a technical role, or a business role?
  2. For some reason, there is a pervasive myth that if you are an expert in a given field, you are qualified to manager others in that field. Nothing could be farther from the truth. It is not a manager's job to be an expert in their field (This is the role of a Subject Matter Expert [SME]), instead it is a mangers job to be an expert in personnel management. Management should be seen as a role parallel to engineering, not a role which is over engineering to which one is promoted.

    This leads to two types of companies: Companies that understand that personnel management is an entirely different discipline than engineering and companies that do not. You want to be working for the former. This is because a manager who was hired for his or her people skills and expertise in organizational management will be far more pleasant to work for than one who was promoted for their technical expertise.

    By inquiring as to the background of your interviewer (who is presumably your prospective manager) you can determine the management philosophy of the company and individual you are interviewing with. If you are not interviewing with your future manager, this should be cause for concern. While it is not uncommon to first interview with a hiring manager or an HR person, at some point during your interview process you should be interviewing with your future manager. If you do not, this is a cause for alarm as you have no idea if you will get along with your future manager and you may not make a good fit for the team that they run.

    If you find that a manager has been promoted for their technical expertise, before you write them off completely ask:

  3. Do you have a favorite book about technology and/or management?
  4. Asking this question will tell you a lot about your future manager. You want to be sure that your manager is an expert in management and has considered their management philosophy. This helps to avoid authoritarian managers who motivate and manage by fear. Far better are those managers who motivate and manage through loyalty. Managers who have not even taken the time to ponder this very basic question about management style are far more likely to be the former type of manager instead of the latter. Managers who motivate employees and manage through loyalty are often far more capable of introspection.

    This question also allows you to determine if your future manager knows anything about management. For example, a manager who has read books like "The Mythical Man Month" or Gene Kim's "The Phoenix Project" and responds with one of these as their favorite tells you a great deal. For example, they know that adding people to a project does not necessarily speed up that project. They know the importance of measuring work and determining where their bottlenecks are and they know the Three Ways of DevOps. Managers like these are a rare unicorn, possessing both technical expertise and capabilities in personnel management. These kinds of managers have probably made a career shift from a technical role to a managerial one - perhaps getting a masters in business by taking night classes.

    On the other hand, if your prospective manager respond with something like Kōnosuke Matsushita's "The Path" or Ken Blanchard and Sheldon Bowles' "Raving Fans" or a similar tome, this tells you something completely different: This manager probably has formal training in management or business, but probably does not have much experience with the challenges in management within the technology field. While they know how to work with and motivate people, they may have very little understanding or sympathy for the difficulties of technology. Explanations of issues or outages may sound like technical jargon or mumbo-jumbo to this person which they may see as just means masking excuses for poor performance in some cases.

    Or, if your interviewer responds with a book from the O'Reilly series, "Beginning Linux Programming" or the "TCP/IP Bible", this tells you that this individual was most likely promoted out of the field and has little experience in management. They may have made very little effort to try to improve as a manager or understand the role that they have been placed into. Instead of managing, this person may demonstrate very little ambition for betterment in their new role and attempt to continue doing the duties of their previous role, leaving their employees without direction or shielding those employees from upper management.

  5. What ticketing system(s) do you use?
  6. While ticketing systems can seem like a drag and a mechanism for "the man" to control every little detail of your time and make sure that they are squeezing every ounce of productivity out of you, believe it or not, ticketing systems are your friend. While tracking your Time-On-Task can seem ominous, it is important that managers keep track of how much of their employee's time is used in order to maintain an optimal Pareto efficiency. Good managers will know that you can't achieve a 100% or more utilization without suffering productivity decreases from their employees and instead will target around 90%.

    But without tracking your time, your manager can't do that. If they don't know that 8 employees are having to achieve a 97% utilization and your backlog of tickets is still growing, they don't know it's time to hire. Simply put, tracking your work helps achieve a good work-life balance for you if your manager is using this data correctly.

    Asking what ticketing system is used tells you a lot. First, if they use 3 ticketing systems as one employer I worked for did, then they are using none of them properly. Conversely, if they have no ticketing system and work out of E-mail, then you can be confident that they don't have it together enough to be able to tell how much work they have and manage their workforce appropriately and in either of these cases your work-life balance will suffer. Finally, a good ticketing system will have the ability to track time against a requester and against the systems impacted. You want to be able to trend which server is constantly breaking, or which annoying users is always putting in tickets and wasting team time. This allows you to identify and remediate trouble areas within your organization or find areas of growth which may need resources directed towards them. Similarly, a good ticketing system will have team queues and allow teams to have a digital Kanban board for managing their workflow.

  7. Do you have a formal Disaster Recovery (DR) plan?
  8. Few things are as important within an IT organization as scalability and redundancy. Scalability ensures that an organization has thought not just at the small scale, but a large scale as well. As things scale out, they sometimes encounter architectural challenges; particularly when scaling geographically. Companies that need to think at large scale generally indicate they have seen growth and are healthy. There is nothing worse than an app hitting a brick wall in terms of scalability and having to pull long hours in order and re-architect an application and to overcome design challenges that could have been prevented by simply doing the job right in the first place

    Similarly, redundancy ensures that in the middle of the night when a server's disk does fill up and the server goes offline or a power outage occurs, you are not receiving phone calls at 3 AM. It guarantees that the application and hardware fabric is robust enough to elegantly handle failures without causing an emergency. This also ensures minimal outages which can be stressful even during business hours, let alone during the middle of the night or during the holidays.

    Asking about a disaster recovery plan is a great way ensure that a company has scalability and redundancy. Ideally, their DR plan will be tested periodically using a DR exercise. I once worked for a company that moved offices and during the office move, they simply simulated a disaster. Everyone went home and telecommuted for the week while a moving company moved the entire office. One week later we all started showing up at the new office and during this time, our customers didn't even notice. They had built a solid enough architecture with enough scalability and redundancy to be able to accommodate that. Not all organizations have.

    A company that has planned for DR is a company that has A) redundancy and B) can scale their redundant environment in an emergency to make sure systems stay online.

  9. How often do you perform test restores?
  10. Often times, backup procedures are not followed - and this is more common than you might think. Asking about test restores ensures that a company has it together enough to A) have backups and B) has the luxury of enough funding and time to actually validate those backups from time to time and ensure they can be restored with minimal-to-no downtime. Backups are like insurance. No one plans for data loss, so this is often the first thing to suffer when staffing gets tight or a team gets overloaded. This can be a canary in a coal mine in terms of how well management has it together. When test restores go out the window, you know that an IT team is beginning to face challenges in either organization or time available to service workload.

  11. What is your inclement weather policy?
  12. While not very important if you live in California, for those who may live in snowy climates or other areas with extreme weather (like hurricanes and tropical storms) it is important to know if your employer is considering your safety. In the IT field, we have the the ability to work from home for a day or two. In fact, some studies show we may even be more productive when working from home.

    Weather-related accidents contribute to 7000 deaths, 800,000 injuries and 45 million dollars of damage each year. An employer who is considering this and tells you to stay home for your safety and work from home instead of coming into the office is bound to be a good employer. This kind of employer sees you as a person, instead of just a mechanism for generating revenue.

  13. Why are you looking to hire? Are you back-filling this position, or is this position new due to growth?
  14. This question is a great way to suss out if a former employee left due to discontent with the position. Ideally a company will be hiring due to growth instead loss of a dissatisfied employee. If the previous employee was unhappy, there is a chance that you will be too. If the employer cannot answer this question due to their HR policies, the most likely cause is that the previous employee was fired for performance-related issues.

  15. What does your unit testing and QA procedure look like?
  16. Inevitably, most IT organizations do at least some amount of in-house development. For some, this is their revenue source. Ensuring that groups follow a full battery of automated testing which validates that all features and functions operate as expected is a mark of a healthy IT department. For those companies that develop a software based product, this kind of testing and Quality Assurance is essential so that the service that the IT team provides is less likely to see outages and bugs.

  17. Do you have a change management process? What does that look like?
  18. While change management may seem like mundane and unnecessary paperwork, it is important to make sure that changes 1) do not collide and 2) happen within convenient time-frames. Unmanaged changes can often happen at times which mean that if there is a problem with a change, you will be woken up at a very inconvenient time once a problem is finally discovered or you may end up working late or during a holiday or vacation due to a failed change. A well defined change management process will go a long way towards ensuring a good work-life balance for employees. The best change management will integrate automated unit testing results and human checks to prevent mistakes. In fact, automated configuration management systems like Puppet or Chef are the single biggest way to prevent mistakes. Machines don't make mistakes; humans do.

  19. What role does this position serve in the company?
  20. Knowing what role your position will serve within a company is very important. Roles that fall under cost centers and expenses within an organization will often be squeezed to reduce expenses and maximize profits. This can often result in sub-par equipment or deploymentswhich ensures that there will be more time spent maintaining and supporting the environment. Conversely, IT departments which are associated with profit centers are responsible for the success and revenue generated by a company. While this can be stressful during outages which will directly impact the bottom line, it also helps to ensure that when IT requests resources that are sincerely needed, these are not denied in a cost saving bid. Furthermore, organizations which silo into Operations, Networking and Development teams can often become embroiled in turf wars. When these teams are consolidated and operate in concert, success for one is success for all groups while headaches for one become headaches for all.

  21. What does your patch management cycle look like
  22. Good organizations should have a patch management system. Great organizations will patch monthly and will ensure that patches go into a QA or staging environments first and systems are checked for issues. Then these patches should be promoted to production. Typically, you will find that unhealthy organizations do not find the time to keep up on this monthly maintenance task.

Hopefully with these questions, you will be able to find an organization which is a good fit for you and will result in a life-long and happy pairing with a company at which you can retire.

Did I miss any questions? Do you have any criticisms of the above questions or additional suggestions? Leave you thoughts in the comments below.

Sunday, November 3, 2013

How I obtained Stock ROMs

The first and most important step to obtaining the stock system ROM is to figure out your partitioning scheme so that you know what you need to and should not backup. To figure this out, you will need to view your Partition Information Table (PIT) - a GPT based descriptor for your NAND flash's logical layout. For the most authoritative source of information that I have found regarding the partitioning on Android phones, check out El Grande Partition thread over at xda-developers.com To get a good sense of your partitioning, you will need both the block device names (used by the mount command) and the actual partition names. This translates roughly into: You need the output of /proc/partitions and parted. Once you have root, getting /proc/partitions is easy. Simply type "cat /proc/partitions":

app_128@android:/ $ su
root@android:/ # cat /proc/partitions

This results in the following output:

major    minor    #blocks        name
17907634944mmcblk0
179161440mmcblk0p1
1792128mmcblk0p2
1793256mmcblk0p3
1794512mmcblk0p4
17952048mmcblk0p5
1796512mmcblk0p6
179710240mmcblk0p7
1798512mmcblk0p8
1799512mmcblk0p9
1791010240mmcblk0p10
1791113952mmcblk0p11
179123072mmcblk0p12
179133072mmcblk0p13
179141536000mmcblk0p14
179155079040mmcblk0p15
179168192mmcblk0p16
17917860160mmcblk0p17
1791810240mmcblk0p18
1791910240mmcblk0p19
179206144mmcblk0p20
179213072mmcblk0p21
179228mmcblk0p22
179235120mmcblk0p23
179642048mmcblk0boot1
179322048mmcblk0boot0

To get the output from parted requires a little more skill as parted is not present on most phones by default and it is not included with busybox. Thanks to the guys over at droidforum however, you can download this from here. Once you have unzipped this file and uploaded it to your device, either by placing it into disk mode, using adb push or by taking out the mini-SD card and putting it into your computer, you can then use parted to dump the partition table:
app_128@android:/ $ su
root@android:/ #cp /mnt/extSdCard/parted
Number    Start            End            SizeFile system    NameFlags
14194kB67.1MB62.9MBmodem
267.1MB67.2MB131kBsbl1
367.2MB67.5MB262kBsbl2
467.5MB68.0MB524kBsbl3
568.0MB70.1MB2097kBaboot
670.1MB70.6MB524kBrpm
770.6MB81.1MB10.5MBboot
881.1MB81.7MB524kBtz
981.7MB82.2MB524kBpad
1082.2MB92.7MB10.5MBparam
1192.7MB107MB14.3MBext4efs
12107MB110MB3146kBmodemst1
13110MB113MB3146kBmodemst2
14113MB1686MB1573MBext4system
151686MB6887MB5201MBext4userdata
166887MB6895MB8389kBext4persist
176895MB7776MB881MBext4cache
187776MB7787MB10.5MBrecovery
197787MB7797MB10.5MBfota
207797MB7804MB6291kBbackup
217804MB7807MB3146kBfsg
227807MB7807MB8192Bssd
237807MB7812MB5243kBgrow

dd if=/dev/block/mmcblk0p1 of=/sdcard/modem.bin
dd if=/dev/block/mmcblk0p2 of=/sdcard/sbl1.mbn
dd if=/dev/block/mmcblk0p3 of=/sdcard/sbl2.mbn
dd if=/dev/block/mmcblk0p4 of=/sdcard/sbl3.mbn
dd if=/dev/block/mmcblk0p5 of=/sdcard/aboot.mbn
dd if=/dev/block/mmcblk0p6 of=/sdcard/rpm.mbn
dd if=/dev/block/mmcblk0p7 of=/sdcard/boot.img
dd if=/dev/block/mmcblk0p8 of=/sdcard/tz.mbn
dd if=/dev/block/mmcblk0p9 of=/sdcard/pad.mbn
dd if=/dev/block/mmcblk0p10 of=/sdcard/param.mbn
dd if=/dev/block/mmcblk0p12 of=/sdcard/modemst1.bin
dd if=/dev/block/mmcblk0p13 of=/sdcard/modemst2.bin
dd if=/dev/block/mmcblk0p16 of=/sdcard/persist.ext4
dd if=/dev/block/mmcblk0p18 of=/sdcard/recovery.img
dd if=/dev/block/mmcblk0p21 of=/sdcard/fsg.mbn
dd if=/dev/block/mmcblk0p23 of=/sdcard/grow.mbn

Restoring the Stratosphere II (SCH-I415) to stock/Unrooting and ROMs, Etc.

To Unroot your phone or restore it to stock, simply follow this post which walks you through rooting but use the images below. On this page, you will find all files relevant to your Stratosphere II (SCH-I415)

Android Version 4.1.2 (MA3)

Stock ROM
Root
Cyanogen Mod
My custom ROM


Android Version 4.0.4 (LJ2)

Stock ROM
Root

PIT file (Partition Information Table)

Download

Please note that the PIT file for 4.1.2 and 4.0.4 are the same. The md5 hash of a PIT file dumped from both versions shows that the table is identical on both versions. For a human readable output of the PIT, Check out this page


Boot files
5e6f0d436f3a266aa9a2a448b526465a I415LJ2_aboot_p5.img
bc6859071a8da0d9fbe9ddf2045a1ce5 aboot.mbn

Note that the USB jig works only on the LJ2 (4.0.4) Bootloader. The MA3 (4.1.2) bootloader appears to be an ICS bootloader. I highly recommend using the LJ2 bootloader as it allows for both ClockworkMod recovery and using the USB jig. The MA3 bootloader allows neither.

Tuesday, September 17, 2013

Stratosphere II (SCH-I415) Teardown

After trying to unlock the SIM on my phone, I managed to brick it. In order to attempt a repair, I needed to take it apart, but to date, I haven't found any teardowns for it, so I figured I might as well be the first. For the record, the USB jig does not seem to work with this model of phone.

Disassembling the back of the phone
This yields the motherboard, keyboard, camera and SD/SIM slot

Step 1: Remove the battery cover and battery to expose the battery compartment. Also remove any SIM cards and SD cards.


(Click to enlarge)

Step 2: Remove the 7 screws that hold the back bezel and the front bezel together


(Click to enlarge)

Step 3: Open the keyboard and gently begin prying between the keyboard and back bezel


(Click to enlarge)

The back bezel should pull away and the keyboard will come lose. It is not screwed down. In this picture the keyboard is actually laying upside down in the keyboard tray. To re-install it properly, you would need to flip it over.


(Click to enlarge)

Step 4: Remove the screws securing the motherboard to the digitizer and hinge and pry up the connector on the right side of the phone.


(Click to enlarge)

Step 5: To remove the heat sink, access the front of the motherboard or replace the SIM/SD slot, pry the connector at the top of the phone up and pry at the four corners of the heat sink.

Board Analysis
In this section, we go over the chips and features of the motherboard. For the digitizer, skip to the next section

At this point, the phone should be in several peices:


(Click to enlarge)

Back view of the board:


(Click to enlarge)

In the above, you can clearly see the contacts for the keyboard.

Front view of the motherboard:


(Click to enlarge)

Another view:


(Click to enlarge)

Sorry, these are the highest resolution photos I have. If you need some numbers off a specific chip, E-mail me and I'll see if I can get them for you. Also see the Galaxy S Relay below

(Click to enlarge)
  1. RTR8600 - Qualcomm multi-band/mode RF transceiver WEDGE

  2. RTR8605 Qualcomm IntelliCeiver transceiver for cellular RF management

  3. KLM8G2FE3B-B001 - Samsung eMMC 8 GB NAND flash

  4. K3PE7e700D-XGC2 - 8gb 500MhZ LPDDR2 RAM 1.2V 2CH x32/ch 220-FBGA, 14x14 PoP, DDP, 128Mx32*2

    MSM8960 - 1.5 GhZ Snapdragon S4 Plus 28 nm ARMv7 Processor with integrated Bluetooth 4.0 and 802.11n (2.4/5 GHz)
    Cache:
    L0: 2x4 KB; L1: 2x16 KB; L2: 1 MB

    The above (RAM and CPU) are integrated onto one chip.
    For a schematic of the above, see this PDF For a full "hack pack" for this chip, check out this link.

  5. PM8921 - Qualcomm PM8921 is a power management IC


This board appears to be extremely similar to the SGH-T699 aka Galaxy S Relay 4G (See this)

Additional information about the SGH-T699:
http://relay4g.wikia.com/wiki/Main_Page
https://github.com/TeamApexQ/apexq/wiki

Other devices that use this board are:
Asus Transformer Pad Infinity (3G/4G version), BlackBerry Z10, HTC Droid Incredible 4G LTE, HTC Evo 4G LTE, HTC One X (North America), HTC One XL, HTC Windows Phone 8X, LG Mach, Motorola Atrix HD, Motorola Droid Razr M, Motorola Droid Razr HD, Motorola Razr HD, Motorola Droid Razr Maxx HD, Nokia Lumia 820, Nokia Lumia 920, Nokia Lumia 925, Nokia Lumia 1020, Panasonic Eluga Power, Samsung Galaxy S III (select versions), Galaxy S Relay 4G, Samsung Photon Q 4G, Sharp Aquos Phone sv (SH-10D), Sharp Aquos Phone Zeta (SH-09D), Sony Xperia GX, Sony Xperia TL, Sony Xperia SX, Sony Xperia V, Toshiba Regza Phone (T-02D), ZTE Grand Era LTE, ZTE Grand X LTE, ZTE V96, Pantech Vega Racer 2 IM-A830L

Digitizer/screen replacement
This is incomplete since I didn't need to replace my digitizer or screen. I didn't go any further than this because I didn't want to have to.

(Click to enlarge)

Step 1: Slide the hinge halfway open to reveal all 6 screws on the back to of the screen and remove the screws


(Click to enlarge)

Step 2: Begin prying the back of the LCD off.


(Click to enlarge)

Step 3: Flip up the hinge on the connector to allow you to disconnect the ribbon cable. This is the only hinged connector on the phone. The others just snap together. This connector is similar in style to the ones pictured here.



Note: Reassembly of the LCD is very tricky as you have to slide the hinge halfway between open and closed in order to leave enough ribbon cable to reconnect to the LCD. In the picture below, I will have to flip my hand over and keep the hinge in it's current position (and try to keep my thumb out of the way) and then try to reattach the connector by reaching my fingers through the space in the sliding hinge to flip the ribbon connector closed. I definitely needed about 5 hands to do this and I didn't get the cable as securely attached as it had been initially. I suspect this cable will be the cause of a lot of failed displays. Here is a picture of me halfway through this process:


(Click to enlarge)

So, a few miscellaneous note:
  • I think I found the JTAG interface. If you can identify the pinouts circled in the graphic above, please E-mail me so I can update this teardown

Friday, May 29, 2009

Creating deb packages manually/Using alien to convert RPMs

Problem:

Although you can create debian packages using an automated installer at compile time using checkinstall at compile time. In this example, we will be using precompiled binary files that need to be ported from RPM packages to deb packages to port a program to a debian based system

Background:

I work for a Managed Service Provider, and we use a product called N-Able to do monitoring for our clients. We monitor servers on all platforms (Windows, Linux and Mac) and accordingly have to be able to install software for this on each operating system. The only issue was, for Linux, there was no Debian agent, (only red hat) so I decided to port it to Debian systems.

Solution:

In order to do this, you will need to have 2 tools installed: 1) alien and 2) ar. I used an Ubuntu 9.04 system for this process, though it should work on any system

Step 1: Convert the RPM to deb

to do this, simply type "alien packagename" You should now have a deb file with the same name as the origional (save for the extension)

Step 2: Extract the data from the deb archive

In this example, most of the files were converted and no additional work was necessary, however 1 of the packages required additional modifications, as the startup scripts it used were written for the red hat style startup/shutdown system using chkconfig, etc. This is very incompatible with Debian based systems. To correct this, I had to extract the deb package, so that I could modify these packages.


Start by creating a directory for each file and copying the deb into the folder:

mkdir nagent
cp nagent-rhel5.1_6.7.0.2169-3_i386.deb ./nagent


Then move into the directory and extract the data from the deb package:

cd nagent
ar x nagent-rhel5.1_6.7.0.2169-3_i386.deb


You should now have 3 additional files: control.tar.gz data.tar.gz and debian-binary
debian-binary can be ignored, as it only has a version # used by ar. This leaves us with data.tar.gz and control.tar.gz. data.tar.gz is all of the binary and configuration files needed by the program. If you extract this file, using tar, you will note that it mirrors the file structure of your base system. This is because dpkg just extracts this file with a working directory of root. In other words, if you have a file in ~/nagent/var, when you run dpkg nagent-rhel5.1_6.7.0.2169-3_i386.deb, after it has been repackaged, whatever files in ~/nagent/var will be extracted to /var on your system. I would reccomend creating a directory in your working directory, called data and extracting the data.tar.gz file in there. For the purposes of my project, I needed to update /etc/init.d/n-agent so that it worked correctly on a Debian system. I will not go into detail on bash scripting here, as that is well beyond the scope of this blog post. You can get some help by checking out /etc/init.d/skeleton on a debian system to get an idea of what your startup script might look like, or any of the other startup scripts in the /etc/init.d directory. After you are done making changes, simply re-compress the files to data.tar.gz and overwrite the one in your nagent (or comparable) directory. Next, comes the control.tar.gz file. In this archive are 6 files. conffiles contains a listing of configuration files. If removed using the dpkg --remove command, config files will be left behind (to remove said files, --purge wold be used.) control contains package information such as a description and dependancies that dpkg should check for before installing. alien should have already updated this for you. The md5sums file should be self explanatory and already taken care of for you by alien as well. The remaining files are used to configure non-static elements of you system at install/removal time. The preinst is a BASH script which runs before the installation of the debain package. Likewise postinst is run after installation. Finally prerm is used before removal. For my purposes, I only needed to modify postinst to add a user used by my program. This was because red hat and debian use different programs to add users.

Step 3: Repackageing your deb file

After updating and rearchiving you data.tar.gz and control.tar.gz directories, and cleaning up any work files/directories you used by deleting them, you must then repackage you deb file. This can be done with the command:

ar cr nagent-rhel5.1_6.7.0.2169-3_i386.deb


You can now run dpkg -i filename to verify that the changes you made worked correctly.

This should be everything you need to port a RPM package to a deb package. Good Luck!

Thursday, May 28, 2009

Dual Head Display

While many mistakenly call Linux servers with dual monitors using xinetrama or twin view "dual head," few know that dual head indicates a setup different than dual monitors. A dual head system has two monitors running two separate window managers or even two separate x sessions.

Problem:
I have two monitors at work, and while I am a Mac and Linux Solutions Engineer with Staples Technology Solutions, Our ticketing system is based on the .net framework, which means that I have to run windows somehow in order to enter tickets. While we can use a terminal server for this, there are other things which are also windows only which I sometimes need to test, and so I really can't do without running 3 OSs daily. I have a Mac laptop, so that takes care of one, but I only have 1 desktop and need two more OSs. Enter VirtualBox (virtualbox.org). With VirtualBox, I am able to run a windows VM on my Linux installation (or vice versa if I wanted). Now, rather than trying to set up two seperate windows managers to run and then run virtualbox on top of one of them, I figured I would save CPU cycles by running KDE on display :0.0 and starting virtualbox directly on display :0.1 without a window manager. I also wanted this because I LOVE compiz--Especially the cube with edge flip. But, I didn't want windows disappearing when I rotated the cube (which would have happened with twinview or xinetrama), so a dual head setup seemed like the perfect solution. Getting this setup however, proved to be harder than one might think...

Solution:
First, the computer I was using was a bit old, and as a result I had a bit of trouble initially when I was trying to use an old Radeon X600 card. Supposedly it was supported by the latest ATI driver, but no such luck. I never could get it to work with the proprietary driver and since the free (as in speech) driver did not support doing dual monitors through the single DVI port (with splitter) I had problems. I work around it by robbing an NV285 card out of one of the other workstations we had lying around and I was in business. Sort of. I didn't have too much trouble configuring my xorg file using the nvidia-xconfig tool. I had to then go into my xorg.conf file and disable twinview (and xinetrama) and I was good to go. Sort of. I fired up KDE and got KDE on one monitor and a blank monitor on next to it. I could move my mouse between them. Excellent. Now to set my startup options. In case you are looking for where to set programs to run at start up in KDE, creating an executable BASH script in ~/.kde/Autostart/ will cause this bash script to be executed at login. So, I wrote this little diddy and I was all set:

compiz &
DISPLAY=":0.1" VBoxManage startvm 'Windows XP' -type gui
Well, sort of all set. Now I noticed that I was getting compiz started on both monitors, even though I did not have a WM on :0.1. With a little research, I figured out that if I edited /usr/bin/compiz edited the COMPIZ_OPTIONS variable to read "COMPIZ_OPTIONS=--replace --ignore-desktop-hints --only-current-screen", it would start on just display :0.0. You can also run "compiz.real --replace --ignore-desktop-hints --only-current-screen &" in your Autostart BASH script. Be careful because compiz may autostart on Ubuntu Jaunty 9.04, so if you start it again from an Autostart script, theings are going to get confusing. I had this problem at first. I forget how I would up stopping KDE from starting compiz at startup. I found the post on the ubuntu forms, but it's not really important. What is important is that this was the absolute wrong thing to do. After doing this, I could not type on :0.1. I could move my mouse over there and select windows, but no keyboard and compiz effects were appalingly slow. So I hit the ubuntu bug tracker and came across bug https://bugs.launchpad.net/ubuntu/+source/compiz/+bug/173517 and bug https://bugs.launchpad.net/ubuntu/+source/desktop-effects/+bug/209229. But how to configure :0.1 to stop that annoying cube behavior? I tried selecting screen 1 in ccsm (Compiz Config Settings Manager) but this just configured compiz on display :0.0. Fortunately, I came across a forum post with a user with the same problem. He was able to resolve his issue by starting ccsm on display :0.1 and then configuring. So I tried to start ccsm on display :0.1. For the record, if you need to start any program on a different display, simply prepend "DISPLAY=":X.X" to your command. I fired up my terminal and ran "DISPLAY=":0.1"ccsm" and it hung trying to load the icons for the program becuase there was no window manager on that monitor to manage the icons. Super annoying. Finally, I edited my /etc/kde4/kdm/kdmrc file and switched which display kdm started on by changing the "StaticServers=:0" to read "StaticServers=:0.1" and restarting kdm (sudo killall kdm followed by sudo kdm). Now, if you do this, be sure to disable compiz on display :0.0 and then calling compiz.real directly using the command "DISPLAY=:0.1 compiz.real --replace --ignore-desktop-hints --only-current-screen &" otherwise, you may mess things up. After this I was able to start ccsm and configure this display so that my cube only had 1 side and no loger rotated (it was appearing as a two sided cube) For some reason some of the options were global (such as edge flip) while others were localized (like the number of sides on the cube). Whatever. I got what I needed. Anyway, after setting everything back so that it displayed correctly and windows started at login, I had what I wanted. And with both monitors running compiz effects (even though :0.1 didn't use them) I was now getting the performance I wanted out of my video card. So if you are having trouble with compiz performance with your nvidia NV285 card (or other card) and are running compiz on only one screen, try running it on both screens.

Ahhh. Much better.

Here is my xorg.conf file in case you are having trouble configuring dual head:
# nvidia-settings: X configuration file generated by nvidia-settings
# nvidia-settings: version 1.0 (buildd@palmer) Sun Feb 1 20:21:04 UTC 2009

# xorg.conf (X.Org X Window System server configuration file)
#
# This file was generated by dexconf, the Debian X Configuration tool, using
# values from the debconf database.
#
# Edit this file with caution, and see the xorg.conf manual page.
# (Type "man xorg.conf" at the shell prompt.)
#
# This file is automatically updated on xserver-xorg package upgrades *only*
# if it has not been modified since the last upgrade of the xserver-xorg
# package.
#
# Note that some configuration settings that could be done previously
# in this file, now are automatically configured by the server and settings
# here are ignored.
#
# If you have edited this file but would like it to be automatically updated
# again, run the following command:
# sudo dpkg-reconfigure -phigh xserver-xorg

Section "ServerLayout"
Identifier "Default Layout"
Screen 0 "Screen0" 0 1024
Screen 1 "Screen1" Above "Screen0"
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Mouse0" "CorePointer"
Option "AllowEmptyInput" "On"
EndSection

Section "Module"
Load "dbe"
Load "extmod"
# Load "type1"
# Load "freetype"
Load "glx"
EndSection

Section "ServerFlags"
Option "Xinerama" "0"
EndSection

Section "InputDevice"
# generated from default
Identifier "Keyboard0"
Driver "keyboard"
EndSection

Section "InputDevice"
# generated from default
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/psaux"
Option "Emulate3Buttons" "no"
Option "ZAxisMapping" "4 5"
EndSection

Section "Monitor"
Identifier "Configured Monitor"
EndSection

Section "Monitor"
Identifier "Monitor0"
VendorName "Unknown"
ModelName "DELL 1704FPT"
HorizSync 30.0 - 81.0
VertRefresh 56.0 - 76.0
EndSection

Section "Monitor"
Identifier "Monitor1"
VendorName "Unknown"
ModelName "DELL 1704FPT"
HorizSync 30.0 - 81.0
VertRefresh 56.0 - 76.0
EndSection

#Section "Device"
# Identifier "Configured Video Device"
# Driver "nvidia"
#EndSection

Section "Device"
Identifier "Device0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BoardName "Quadro NVS 280 PCI-E"
BusID "PCI:1:0:0"
Screen 0
# Force one level when AC power
Option "RegistryDwords" "PowerMizerLevelAC=0x3"
#Enable overclocking
Option "Coolbits" "1"

Option "RenderAccel" "True"
Option "TripleBuffer" "True"
Option "DamageEvents" "True"
Option "NoDRI"
EndSection

Section "Device"
Identifier "Device1"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BoardName "Quadro NVS 280 PCI-E"
BusID "PCI:1:0:0"
Screen 1
# Force one level when AC power
Option "RegistryDwords" "PowerMizerLevelAC=0x3"

#Enable overclocking
Option "Coolbits" "1"
Option "RenderAccel" "True"
Option "TripleBuffer" "True"
Option "DamageEvents" "True"
Option "NoDRI"
EndSection

Section "Screen"
Identifier "Default Screen"
Device "Configured Video Device"
Monitor "Configured Monitor"
DefaultDepth 24
EndSection

Section "Screen"
Identifier "Screen0"
Device "Device0"
Monitor "Monitor0"
DefaultDepth 24
Option "TwinView" "0"
Option "TwinViewXineramaInfoOrder" "CRT-0"
Option "metamodes" "CRT-0: nvidia-auto-select +0+0"
SubSection "Display"
Depth 24
EndSubSection
EndSection

Section "Screen"
Identifier "Screen1"
Device "Device1"
Monitor "Monitor1"
DefaultDepth 24
Option "TwinView" "0"
Option "metamodes" "CRT-1: nvidia-auto-select +0+0"
SubSection "Display"
Depth 24
EndSubSection
EndSection

Section "Extensions"
Option "Composite" "Enable"
EndSection

Wednesday, May 27, 2009

Displaying only posts with a certain tag on your blogger homepage

Problem:

Part of why I am creating this blog is so that if/when I am looking for a new job, those who are potentially Googling me will be able to see projects that I have worked on or am working on in order to impress prospective employers (In addition to wanting to potentially help others and also so that I may reference work I did in the past if needed). On the other hand, there may be things such as religion, philosophy or politics that I wish to blog about which might detract or prevent me from getting hired (Even in spite of non-discrimination laws. When I was in sales I learned [via my sales figures] that things can subconsciously affect our decisions, which is why I always dressed nice) As such, I wanted to be able to have the front page of my blog only display articles which were tagged with "Computers" while being able to hide hide other posts when the time comes for me to get a job.

Solution:

I was miffed on how to do this for at first. I tried deleting (manually) the main pane for posts on the front page and replacing this with the URL to page which searches for posts by tag, however, this just caused problems with the main page. I was at a loss until, after a bit of research I found the following blog post: http://blog.mobocracy.net/2007/06/filtering-blogger-by-label.html

Now, in case this link becomes defunct, here is the gist of how to properly apply this fix:

"From your blog dashboard, click on "Layout" then click on "Edit HTML". Once you have done that, click the "Expand Widget Templates" checkbox. Now you're ready to edit. As always, I recommend making a backup before you get started. First, search for the line that starts with . You are going to remove everything in that function between and the closing tag with the following:


< b:if cond="'data:blog.url">
< b:if cond="'data:post.labels'">
< b:loop values="'data:post.labels'" var="'label'">
< b:if cond="'data:label.name">
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< /b:if>
< b:else/>
< b:include data="'post'" name="'printPosts'/">
< /b:if>


The above change essentially says the following: if you are on the homepage, and a post has labels, and the post has the "main" label, then call printPosts. Or, if you are not on the homepage, call printPosts. Now search for the close tag of the main function (it looks like </b:includable>) and after it paste the following code:

< b:includable id="'printPosts'" var="'post'">
< b:if cond="'data:post.dateHeader'">
< h2 class="'date-header'">< data:post.dateheader/>< /h2>
< /b:if>

< b:include data="'post'" name="'post'/">

< b:if cond="'data:blog.pageType">
< b:include data="'post'" name="'comments'/">
< /b:if>
< /b:includable>

The above code is your printPosts function/method. It is identical to what you had before, it was just turned into a function to reduce code duplication. That's it. Now only posts with a "main" label will show up on the front page. For Peter Sachs, who was wanting to filter out content, just change the (== "main") markup to something like (!= "nsfw"). Enjoy."

Note: don't forget the "Expand widget templates" checkbox or else you will not be able to find the sections referenced.

It took me a bit of time to figure out where the code was supposed to go exactly and how it was to be formatted, so I am including a complete working example taken from html code for this blog below:


< ?xml version="1.0" encoding="UTF-8" ?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
< html dir="'data:blog.languageDirection'" xmlns="'http://www.w3.org/1999/xhtml'" b="'http://www.google.com/2005/gml/b'" data="'http://www.google.com/2005/gml/data'" expr="'http://www.google.com/2005/gml/expr'">
< head>
< b:include data="'blog'" name="'all-head-content'/">
< title>< data:blog.pagetitle/>< /title>
< b:skin>< ![CDATA[/*
-----------------------------------------------
Blogger Template Style
Name: Minima
Designer: Douglas Bowman
URL: www.stopdesign.com
Date: 26 Feb 2004
Updated by: Blogger Team
----------------------------------------------- */

/* Variable definitions
====================
< Variable name="bgcolor" description="Page Background Color"
type="color" default="#fff" value="#ffffff">
< Variable name="textcolor" description="Text Color"
type="color" default="#333" value="#333333">
< Variable name="linkcolor" description="Link Color"
type="color" default="#58a" value="#5588aa">
< Variable name="pagetitlecolor" description="Blog Title Color"
type="color" default="#666" value="#666666">
< Variable name="descriptioncolor" description="Blog Description Color"
type="color" default="#999" value="#999999">
< Variable name="titlecolor" description="Post Title Color"
type="color" default="#c60" value="#cc6600">
< Variable name="bordercolor" description="Border Color"
type="color" default="#ccc" value="#cccccc">
< Variable name="sidebarcolor" description="Sidebar Title Color"
type="color" default="#999" value="#999999">
< Variable name="sidebartextcolor" description="Sidebar Text Color"
type="color" default="#666" value="#666666">
< Variable name="visitedlinkcolor" description="Visited Link Color"
type="color" default="#999" value="#999999">
< Variable name="bodyfont" description="Text Font"
type="font" default="normal normal 100% Georgia, Serif" value="normal normal 100% Georgia, Serif">
< Variable name="headerfont" description="Sidebar Title Font"
type="font"
default="normal normal 78% 'Trebuchet MS',Trebuchet,Arial,Verdana,Sans-serif" value="normal normal 78% 'Trebuchet MS',Trebuchet,Arial,Verdana,Sans-serif">
< Variable name="pagetitlefont" description="Blog Title Font"
type="font"
default="normal normal 200% Georgia, Serif" value="normal normal 200% Georgia, Serif">
< Variable name="descriptionfont" description="Blog Description Font"
type="font"
default="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif" value="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif">
< Variable name="postfooterfont" description="Post Footer Font"
type="font"
default="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif" value="normal normal 78% 'Trebuchet MS', Trebuchet, Arial, Verdana, Sans-serif">
< Variable name="startSide" description="Side where text starts in blog language"
type="automatic" default="left" value="left">
< Variable name="endSide" description="Side where text ends in blog language"
type="automatic" default="right" value="right">
*/

/* Use this with templates/template-twocol.html */

body {
background:$bgcolor;
margin:0;
color:$textcolor;
font:x-small Georgia Serif;
font-size/* */:/**/small;
font-size: /**/small;
text-align: center;
}
a:link {
color:$linkcolor;
text-decoration:none;
}
a:visited {
color:$visitedlinkcolor;
text-decoration:none;
}
a:hover {
color:$titlecolor;
text-decoration:underline;
}
a img {
border-width:0;
}

/* Header
-----------------------------------------------
*/

#header-wrapper {
width:660px;
margin:0 auto 10px;
border:1px solid $bordercolor;
}

#header-inner {
background-position: center;
margin-left: auto;
margin-right: auto;
}

#header {
margin: 5px;
border: 1px solid $bordercolor;
text-align: center;
color:$pagetitlecolor;
}

#header h1 {
margin:5px 5px 0;
padding:15px 20px .25em;
line-height:1.2em;
text-transform:uppercase;
letter-spacing:.2em;
font: $pagetitlefont;
}

#header a {
color:$pagetitlecolor;
text-decoration:none;
}

#header a:hover {
color:$pagetitlecolor;
}

#header .description {
margin:0 5px 5px;
padding:0 20px 15px;
max-width:700px;
text-transform:uppercase;
letter-spacing:.2em;
line-height: 1.4em;
font: $descriptionfont;
color: $descriptioncolor;
}

#header img {
margin-$startSide: auto;
margin-$endSide: auto;
}


/* Outer-Wrapper
----------------------------------------------- */
#outer-wrapper {
width: 660px;
margin:0 auto;
padding:10px;
text-align:$startSide;
font: $bodyfont;
}

#main-wrapper {
width: 410px;
float: $startSide;
word-wrap: break-word; /* fix for long text breaking sidebar float in IE */
overflow: hidden; /* fix for long non-text content breaking IE sidebar float */
}

#sidebar-wrapper {
width: 220px;
float: $endSide;
word-wrap: break-word; /* fix for long text breaking sidebar float in IE */
overflow: hidden; /* fix for long non-text content breaking IE sidebar float */
}


/* Headings
----------------------------------------------- */

h2 {
margin:1.5em 0 .75em;
font:$headerfont;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.2em;
color:$sidebarcolor;
}


/* Posts
-----------------------------------------------
*/
h2.date-header {
margin:1.5em 0 .5em;
}

.post {
margin:.5em 0 1.5em;
border-bottom:1px dotted $bordercolor;
padding-bottom:1.5em;
}
.post h3 {
margin:.25em 0 0;
padding:0 0 4px;
font-size:140%;
font-weight:normal;
line-height:1.4em;
color:$titlecolor;
}

.post h3 a, .post h3 a:visited, .post h3 strong {
display:block;
text-decoration:none;
color:$titlecolor;
font-weight:normal;
}

.post h3 strong, .post h3 a:hover {
color:$textcolor;
}

.post-body {
margin:0 0 .75em;
line-height:1.6em;
}

.post-body blockquote {
line-height:1.3em;
}

.post-footer {
margin: .75em 0;
color:$sidebarcolor;
text-transform:uppercase;
letter-spacing:.1em;
font: $postfooterfont;
line-height: 1.4em;
}

.comment-link {
margin-$startSide:.6em;
}
.post img {
padding:4px;
border:1px solid $bordercolor;
}
.post blockquote {
margin:1em 20px;
}
.post blockquote p {
margin:.75em 0;
}

/* Comments
----------------------------------------------- */
#comments h4 {
margin:1em 0;
font-weight: bold;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.2em;
color: $sidebarcolor;
}

#comments-block {
margin:1em 0 1.5em;
line-height:1.6em;
}
#comments-block .comment-author {
margin:.5em 0;
}
#comments-block .comment-body {
margin:.25em 0 0;
}
#comments-block .comment-footer {
margin:-.25em 0 2em;
line-height: 1.4em;
text-transform:uppercase;
letter-spacing:.1em;
}
#comments-block .comment-body p {
margin:0 0 .75em;
}
.deleted-comment {
font-style:italic;
color:gray;
}

#blog-pager-newer-link {
float: $startSide;
}

#blog-pager-older-link {
float: $endSide;
}

#blog-pager {
text-align: center;
}

.feed-links {
clear: both;
line-height: 2.5em;
}

/* Sidebar Content
----------------------------------------------- */
.sidebar {
color: $sidebartextcolor;
line-height: 1.5em;
}

.sidebar ul {
list-style:none;
margin:0 0 0;
padding:0 0 0;
}
.sidebar li {
margin:0;
padding-top:0;
padding-$endSide:0;
padding-bottom:.25em;
padding-$startSide:15px;
text-indent:-15px;
line-height:1.5em;
}

.sidebar .widget, .main .widget {
border-bottom:1px dotted $bordercolor;
margin:0 0 1.5em;
padding:0 0 1.5em;
}

.main .Blog {
border-bottom-width: 0;
}


/* Profile
----------------------------------------------- */
.profile-img {
float: $startSide;
margin-top: 0;
margin-$endSide: 5px;
margin-bottom: 5px;
margin-$startSide: 0;
padding: 4px;
border: 1px solid $bordercolor;
}

.profile-data {
margin:0;
text-transform:uppercase;
letter-spacing:.1em;
font: $postfooterfont;
color: $sidebarcolor;
font-weight: bold;
line-height: 1.6em;
}

.profile-datablock {
margin:.5em 0 .5em;
}

.profile-textblock {
margin: 0.5em 0;
line-height: 1.6em;
}

.profile-link {
font: $postfooterfont;
text-transform: uppercase;
letter-spacing: .1em;
}

/* Footer
----------------------------------------------- */
#footer {
width:660px;
clear:both;
margin:0 auto;
padding-top:15px;
line-height: 1.6em;
text-transform:uppercase;
letter-spacing:.1em;
text-align: center;
}
]]>< /b:skin>
< /head>

< body>
< div id="'outer-wrapper'">< div id="'wrap2'">

< !-- skip links for text browsers -->
< span id="'skiplinks'" style="'display:none;'">
< a href="'#main'">skip to main < /a> |
< a href="'#sidebar'">skip to sidebar< /a>
< /span>

< div id="'header-wrapper'">
< b:section class="'header'" id="'header'" maxwidgets="'1'" showaddelement="'no'">
< b:widget id="'Header1'" locked="'true'" title="'Nerdy" type="'Header'">
< b:includable id="'main'">

< b:if cond="'data:useImage'">
< b:if cond="'data:imagePlacement">
< !--Show just the image, no text-->
< div id="'header-inner'">
< a href="'data:blog.homepageUrl'" style="'display:">
< img alt="'data:title'" height="'data:height'" id="'data:widget.instanceId" src="'data:sourceUrl'" width="'data:width'" style="'display:" />
< /a>
< /div>
< b:else/>
< !--
Show image as background to text. You can't really calculate the width
reliably in JS because margins are not taken into account by any of
clientWidth, offsetWidth or scrollWidth, so we don't force a minimum
width if the user is using shrink to fit.
This results in a margin-width's worth of pixels being cropped. If the
user is not using shrink to fit then we expand the header.
-->
< div style="'"background-image:" id="'header-inner'">
< div class="'titlewrapper'" style="'background:">
< h1 class="'title'" style="'background:">
< b:include name="'title'/">
< /h1>
< /div>
< b:include name="'description'/">
< /div>
< /b:if>
< b:else/>
< !--No header image -->
< div id="'header-inner'">
< div class="'titlewrapper'">
< h1 class="'title'">
< b:include name="'title'/">
< /h1>
< /div>
< b:include name="'description'/">
< /div>
< /b:if>
< /b:includable>
< b:includable id="'title'">
< b:if cond="'data:blog.url">
< data:title/>
< b:else/>
< a href="'data:blog.homepageUrl'">< data:title/>< /a>
< /b:if>
< /b:includable>
< b:includable id="'description'">
< div class="'descriptionwrapper'">
< p class="'description'">< span>< data:description/>< /span>< /p>
< /div>
< /b:includable>
< /b:widget>
< /b:section>
< /div>

< div id="'content-wrapper'">

< div id="'crosscol-wrapper'" style="'text-align:center'">
< b:section class="'crosscol'" id="'crosscol'" showaddelement="'no'/">
< /div>

< div id="'main-wrapper'">
< b:section class="'main'" id="'main'" showaddelement="'no'">
< b:widget id="'Blog1'" locked="'true'" title="'Blog" type="'Blog'">
< b:includable id="'nextprev'">
< div class="'blog-pager'" id="'blog-pager'">
< b:if cond="'data:newerPageUrl'">
< span id="'blog-pager-newer-link'">
< a class="'blog-pager-newer-link'" href="'data:newerPageUrl'" id="'data:widget.instanceId" title="'data:newerPageTitle'">< data:newerpagetitle/>< /a>
< /span>
< /b:if>

< b:if cond="'data:olderPageUrl'">
< span id="'blog-pager-older-link'">
< a class="'blog-pager-older-link'" href="'data:olderPageUrl'" id="'data:widget.instanceId" title="'data:olderPageTitle'">< data:olderpagetitle/>< /a>
< /span>
< /b:if>

< b:if cond="'data:blog.homepageUrl">
< a class="'home-link'" href="'data:blog.homepageUrl'">< data:homemsg/>< /a>
< b:else/>
< b:if cond="'data:newerPageUrl'">
< a class="'home-link'" href="'data:blog.homepageUrl'">< data:homemsg/>< /a>
< /b:if>
< /b:if>

< /div>
< div class="'clear'/">
< /b:includable>
< b:includable id="'backlinks'" var="'post'">
< a name="'links'/">< h4>< data:post.backlinkslabel/>< /h4>
< b:if cond="'data:post.numBacklinks">
< dl class="'comments-block'" id="'comments-block'">
< b:loop values="'data:post.backlinks'" var="'backlink'">
< div class="'collapsed-backlink">
< dt class="'comment-title'">
< span class="'backlink-toggle-zippy'"> < /span>
< a href="'data:backlink.url'" rel="'nofollow'">< data:backlink.title/>< /a>
< b:include data="'backlink'" name="'backlinkDeleteIcon'/">
< /dt>
< dd class="'comment-body">
< data:backlink.snippet/>
< /dd>
< dd class="'comment-footer">
< span class="'comment-author'">< data:post.authorlabel/> < data:backlink.author/>< /span>
< span class="'comment-timestamp'">< data:post.timestamplabel/> < data:backlink.timestamp/>< /span>
< /dd>
< /div>
< /b:loop>
< /dl>
< /b:if>
< p class="'comment-footer'">
< a class="'comment-link'" href="'data:post.createLinkUrl'" id="'data:widget.instanceId" target="'_blank'">< data:post.createlinklabel/>< /a>
< /p>
< /b:includable>
< b:includable id="'post'" var="'post'">
< div class="'post">
< a name="'data:post.id'/">
< b:if cond="'data:post.title'">
< h3 class="'post-title">
< b:if cond="'data:post.link'">
< a href="'data:post.link'">< data:post.title/>< /a>
< b:else/>
< b:if cond="'data:post.url'">
< a href="'data:post.url'">< data:post.title/>< /a>
< b:else/>
< data:post.title/>
< /b:if>
< /b:if>
< /h3>
< /b:if>

< div class="'post-header-line-1'/">

< div class="'post-body">
< data:post.body/>
< div style="'clear:"> < !-- clear for photos floats -->
< /div>

< b:if cond="'data:post.hasJumpLink'">
< div class="'jump-link'">
< a href="'data:post.url">< data:post.jumptext/>< /a>
< /div>
< /b:if>

< div class="'post-footer'">
< div class="'post-footer-line">
< span class="'post-author">
< b:if cond="'data:top.showAuthor'">
< data:top.authorlabel/>
< span class="'fn'">< data:post.author/>< /span>
< /b:if>
< /span>

< span class="'post-timestamp'">
< b:if cond="'data:top.showTimestamp'">
< data:top.timestamplabel/>
< b:if cond="'data:post.url'">
< a class="'timestamp-link'" href="'data:post.url'" rel="'bookmark'" title="'permanent">< abbr class="'published'" title="'data:post.timestampISO8601'">< data:post.timestamp/>< /abbr>< /a>
< /b:if>
< /b:if>
< /span>

< span class="'reaction-buttons'">
< b:if cond="'data:top.showReactions'">
< table border="'0'" cellpadding="'0'" cellspacing="'0'" width="'100%'">< tr>
< td class="'reactions-label-cell'" nowrap="'nowrap'" valign="'top'" width="'1%'">
< span class="'reactions-label'">
< data:top.reactionslabel/>< /span> < /td>
< td>< iframe allowtransparency="'true'" class="'reactions-iframe'" src="'data:post.reactionsUrl'" frameborder="'0'" name="'reactions'" scrolling="'no'/">< /td>
< /tr>< /table>
< /b:if>
< /span>

< span class="'star-ratings'">
< b:if cond="'data:top.showStars'">
< div color="'data:backgroundColor'" color="'data:textColor'" url="'data:post.absoluteUrl'" height="'42'" type="'RatingPanel'" width="'280'/">
< /b:if>
< /span>

< span class="'post-comment-link'">
< b:if cond="'data:blog.pageType">
< b:if cond="'data:post.allowComments'">
< a class="'comment-link'" href="'data:post.addCommentUrl'" onclick="'data:post.addCommentOnclick'">< b:if cond="'data:post.numComments">1 < data:top.commentlabel/>< b:else/>< data:post.numcomments/> < data:top.commentlabelplural/>< /b:if>< /a>
< /b:if>
< /b:if>
< /span>

< !-- backlinks -->
< span class="'post-backlinks">
< b:if cond="'data:blog.pageType">
< b:if cond="'data:post.showBacklinks'">
< a class="'comment-link'" href="'data:post.url">< data:top.backlinklabel/>< /a>
< /b:if>
< /b:if>
< /span>

< span class="'post-icons'">
< !-- email post links -->
< b:if cond="'data:post.emailPostUrl'">
< span class="'item-action'">
< a href="'data:post.emailPostUrl'" title="'data:top.emailPostMsg'">
< img alt="''" class="'icon-action'" height="'13'" src="'http://www.blogger.com/img/icon18_email.gif'" width="'18'/" />
< /a>
< /span>
< /b:if>

< !-- quickedit pencil -->
< b:include data="'post'" name="'postQuickEdit'/">
< /span>
< /div>

< div class="'post-footer-line">
< span class="'post-labels'">
< b:if cond="'data:post.labels'">
< data:postlabelslabel/>
< b:loop values="'data:post.labels'" var="'label'">
< a href="'data:label.url'" rel="'tag'">< data:label.name/>< /a>< b:if cond="'data:label.isLast">,< /b:if>
< /b:loop>
< /b:if>
< /span>
< /div>

< div class="'post-footer-line">
< span class="'post-location'">
< b:if cond="'data:top.showLocation'">
< b:if cond="'data:post.location'">
< data:postlocationlabel/>
< a href="'data:post.location.mapsUrl'" target="'_blank'">< data:post.location.name/>< /a>
< /b:if>
< /b:if>
< /span>
< /div>
< /div>
< /div>
< /b:includable>
< b:includable id="'status-message'">
< b:if cond="'data:navMessage'">
< div class="'status-msg-wrap'">
< div class="'status-msg-body'">
< data:navmessage/>
< /div>
< div class="'status-msg-border'">
< div class="'status-msg-bg'">
< div class="'status-msg-hidden'">< data:navmessage/>< /div>
< /div>
< /div>
< /div>
< div style="'clear:">
< /b:if>
< /b:includable>
< b:includable id="'comment-form'" var="'post'">
< div class="'comment-form'">
< a name="'comment-form'/">
< h4 id="'comment-post-message'">< data:postcommentmsg/>< /h4>
< p>< data:blogcommentmessage/>< /p>
< data:blogteamblogmessage/>
< a href="'data:post.commentFormIframeSrc'" id="'comment-editor-src'/">
< iframe allowtransparency="'true'" class="'blogger-iframe-colorize" frameborder="'0'" height="'275'" id="'comment-editor'" name="'comment-editor'" scrolling="'no'" src="''" width="'100%'/">
< data:post.friendconnectjs/>
< data:post.cmtfpiframe/>
< script type="'text/javascript'">
BLOG_CMT_createIframe('< data:post.apprpcrelaypath/>', '< data:post.communityid/>');
< /script>
< /div>
< /b:includable>
< b:includable id="'backlinkDeleteIcon'" var="'backlink'">
< span class="'"item-control">
< a href="'data:backlink.deleteUrl'" title="'data:top.deleteBacklinkMsg'">
< img src="'http://www.blogger.com/img/icon_delete13.gif'/" />
< /a>
< /span>
< /b:includable>
< b:includable id="'postQuickEdit'" var="'post'">
< b:if cond="'data:post.editUrl'">
< span class="'"item-control">
< a href="'data:post.editUrl'" title="'data:top.editPostMsg'">
< img alt="''" class="'icon-action'" height="'18'" src="'http://www.blogger.com/img/icon18_edit_allbkg.gif'" width="'18'/" />
< /a>
< /span>
< /b:if>
< /b:includable>
< b:includable id="'main'" var="'top'">
< !-- posts -->
< div class="'blog-posts">

< b:include data="'top'" name="'status-message'/">

< data:adstart/>
< b:loop values="'data:posts'" var="'post'">
< b:if cond="'data:blog.url">
< b:if cond="'data:post.labels'">
< b:loop values="'data:post.labels'" var="'label'">
< b:if cond="'data:label.name">
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< /b:if>
< b:else/>
< b:include data="'post'" name="'printPosts'/">
< /b:if>
< /b:loop>
< data:adend/>
< /div>

< !-- navigation -->
< b:include name="'nextprev'/">

< !-- feed links -->
< b:include name="'feedLinks'/">

< b:if cond="'data:top.showStars'">
< script src="'http://www.google.com/jsapi'" type="'text/javascript'/">
< script type="'text/javascript'">
google.load("annotations", "1", {"locale": "< data:top.languagecode/>"});
function initialize() {
google.annotations.setApplicationId(< data:top.blogspotreviews/>);
google.annotations.createAll();
google.annotations.fetch();
}
google.setOnLoadCallback(initialize);
< /script>
< /b:if>

< /b:includable>
< b:includable id="'commentDeleteIcon'" var="'comment'">
< span class="'"item-control">
< a href="'data:comment.deleteUrl'" title="'data:top.deleteCommentMsg'">
< img src="'http://www.blogger.com/img/icon_delete13.gif'/" />
< /a>
< /span>
< /b:includable>
< b:includable id="'printPosts'" var="'post'">
< b:if cond="'data:post.dateHeader'">
< h2 class="'date-header'">< data:post.dateheader/>< /h2>
< /b:if>

< b:include data="'post'" name="'post'/">

< b:if cond="'data:blog.pageType">
< b:include data="'post'" name="'comments'/">
< /b:if>
< /b:includable>
< b:includable id="'feedLinks'">
< b:if cond="'data:blog.pageType"> < !-- Blog feed links -->
< b:if cond="'data:feedLinks'">
< div class="'blog-feeds'">
< b:include data="'feedLinks'" name="'feedLinksBody'/">
< /div>
< /b:if>

< b:else/> < !--Post feed links -->
< div class="'post-feeds'">
< b:loop values="'data:posts'" var="'post'">
< b:if cond="'data:post.allowComments'">
< b:if cond="'data:post.feedLinks'">
< b:include data="'post.feedLinks'" name="'feedLinksBody'/">
< /b:if>
< /b:if>
< /b:loop>
< /div>
< /b:if>
< /b:includable>
< b:includable id="'feedLinksBody'" var="'links'">
< div class="'feed-links'">
< data:feedlinksmsg/>
< b:loop values="'data:links'" var="'f'">
< a class="'feed-link'" href="'data:f.url'" type="'data:f.mimeType'" target="'_blank'">< data:f.name/> (< data:f.feedtype/>)< /a>
< /b:loop>
< /div>
< /b:includable>
< b:includable id="'comments'" var="'post'">
< div class="'comments'" id="'comments'">
< a name="'comments'/">
< b:if cond="'data:post.allowComments'">
< h4>
< b:if cond="'data:post.numComments">
1 < data:commentlabel/>:
< b:else/>
< data:post.numcomments/> < data:commentlabelplural/>:
< /b:if>
< /h4>

< b:if cond="'data:post.commentPagingRequired'">
< span class="'paging-control-container'">
< a class="'data:post.oldLinkClass'" href="'data:post.oldestLinkUrl'">< data:post.oldestlinktext/>< /a>
 
< a class="'data:post.oldLinkClass'" href="'data:post.olderLinkUrl'">< data:post.olderlinktext/>< /a>
 
< data:post.commentrangetext/>
 
< a class="'data:post.newLinkClass'" href="'data:post.newerLinkUrl'">< data:post.newerlinktext/>< /a>
 
< a class="'data:post.newLinkClass'" href="'data:post.newestLinkUrl'">< data:post.newestlinktext/>< /a>
< /span>
< /b:if>

< dl id="'comments-block'">
< b:loop values="'data:post.comments'" var="'comment'">
< dt class="'"comment-author" id="'data:comment.anchorName'">
< b:if cond="'data:comment.favicon'">
< img src="'data:comment.favicon'" height="'16px'" style="'margin-bottom:-2px;'" width="'16px'/" />
< /b:if>
< a name="'data:comment.anchorName'/">
< b:if cond="'data:comment.authorUrl'">
< a href="'data:comment.authorUrl'" rel="'nofollow'">< data:comment.author/>< /a>
< b:else/>
< data:comment.author/>
< /b:if>
< data:commentpostedbymsg/>
< /dt>
< dd class="'comment-body'">
< b:if cond="'data:comment.isDeleted'">
< span class="'deleted-comment'">< data:comment.body/>< /span>
< b:else/>
< p>< data:comment.body/>< /p>
< /b:if>
< /dd>
< dd class="'comment-footer'">
< span class="'comment-timestamp'">
< a href="'data:comment.url'" title="'comment">
< data:comment.timestamp/>
< /a>
< b:include data="'comment'" name="'commentDeleteIcon'/">
< /span>
< /dd>
< /b:loop>
< /dl>

< b:if cond="'data:post.commentPagingRequired'">
< span class="'paging-control-container'">
< a class="'data:post.oldLinkClass'" href="'data:post.oldestLinkUrl'">
< data:post.oldestlinktext/>
< /a>
< a class="'data:post.oldLinkClass'" href="'data:post.olderLinkUrl'">
< data:post.olderlinktext/>
< /a>
 
< data:post.commentrangetext/>
 
< a class="'data:post.newLinkClass'" href="'data:post.newerLinkUrl'">
< data:post.newerlinktext/>
< /a>
< a class="'data:post.newLinkClass'" href="'data:post.newestLinkUrl'">
< data:post.newestlinktext/>
< /a>
< /span>
< /b:if>

< p class="'comment-footer'">
< b:if cond="'data:post.embedCommentForm'">
< b:if cond="'data:post.allowNewComments'">
< b:include data="'post'" name="'comment-form'/">
< b:else/>
< data:post.nonewcommentstext/>
< /b:if>
< b:else/>
< b:if cond="'data:post.allowComments'">
< a href="'data:post.addCommentUrl'" onclick="'data:post.addCommentOnclick'">< data:postcommentmsg/>< /a>
< /b:if>
< /b:if>

< /p>
< /b:if>

< div id="'backlinks-container'">
< div id="'data:widget.instanceId">
< b:if cond="'data:post.showBacklinks'">
< b:include data="'post'" name="'backlinks'/">
< /b:if>
< /div>
< /div>
< /div>
< /b:includable>
< /b:widget>
< /b:section>
< /div>

< div id="'sidebar-wrapper'">
< b:section class="'sidebar'" id="'sidebar'" preferred="'yes'">
< b:widget id="'BlogArchive1'" locked="'false'" title="'Blog" type="'BlogArchive'">
< b:includable id="'main'">
< b:if cond="'data:title'">
< h2>< data:title/>< /h2>
< /b:if>
< div class="'widget-content'">
< div id="'ArchiveList'">
< div id="'data:widget.instanceId">
< b:if cond="'data:style">
< b:include data="'data'" name="'interval'/">
< /b:if>
< b:if cond="'data:style">
< b:include data="'data'" name="'flat'/">
< /b:if>
< b:if cond="'data:style">
< b:include data="'data'" name="'menu'/">
< /b:if>
< /div>
< /div>
< b:include name="'quickedit'/">
< /div>
< /b:includable>
< b:includable id="'flat'" var="'data'">
< ul>
< b:loop values="'data:data'" var="'i'">
< li class="'archivedate'">
< a href="'data:i.url'">< data:i.name/>< /a> (< data:i.post-count/>)
< /li>
< /b:loop>
< /ul>
< /b:includable>
< b:includable id="'menu'" var="'data'">
< select id="'data:widget.instanceId">
< option value="''">< data:title/>< /option>
< b:loop values="'data:data'" var="'i'">
< option value="'data:i.url'">< data:i.name/> (< data:i.post-count/>)< /option>
< /b:loop>
< /select>
< /b:includable>
< b:includable id="'interval'" var="'intervalData'">
< b:loop values="'data:intervalData'" var="'i'">
< ul>
< li class="'"archivedate">
< b:include data="'i'" name="'toggle'/">
< a class="'post-count-link'" href="'data:i.url'">< data:i.name/>< /a>
< span class="'post-count'" dir="'ltr'">(< data:i.post-count/>)< /span>
< b:if cond="'data:i.data'">
< b:include data="'i.data'" name="'interval'/">
< /b:if>
< b:if cond="'data:i.posts'">
< b:include data="'i.posts'" name="'posts'/">
< /b:if>
< /li>
< /ul>
< /b:loop>
< /b:includable>
< b:includable id="'toggle'" var="'interval'">
< b:if cond="'data:interval.toggleId'">
< b:if cond="'data:interval.expclass">
< a class="'toggle'" href="'data:widget.actionUrl" action="toggle"" dir="close&toggle=""" toggleopen=""">
< span class="'zippy">▼ < /span>
< /a>
< b:else/>
< a class="'toggle'" href="'data:widget.actionUrl" action="toggle"" dir="open&toggle=""" toggleopen=""">
< span class="'zippy'">
< b:if cond="'data:blog.languageDirection">
◄
< b:else/>
►
< /b:if>
< /span>
< /a>
< /b:if>
< /b:if>
< /b:includable>
< b:includable id="'posts'" var="'posts'">
< ul class="'posts'">
< b:loop values="'data:posts'" var="'i'">
< li>< a href="'data:i.url'">< data:i.title/>< /a>< /li>
< /b:loop>
< /ul>
< /b:includable>
< /b:widget>
< b:widget id="'Profile1'" locked="'false'" title="'About" type="'Profile'">
< b:includable id="'main'">
< b:if cond="'data:title">
< h2>< data:title/>< /h2>
< /b:if>
< div class="'widget-content'">
< b:if cond="'data:team"> < !-- team blog profile -->
< ul>
< b:loop values="'data:authors'" var="'i'">
< li>< a href="'data:i.userUrl'">< data:i.display-name/>< /a>< /li>
< /b:loop>
< /ul>

< b:else/> < !-- normal blog profile -->

< b:if cond="'data:photo.url">
< a href="'data:userUrl'">< img class="'profile-img'" alt="'data:photo.alt'" height="'data:photo.height'" src="'data:photo.url'" width="'data:photo.width'/" />< /a>
< /b:if>

< dl class="'profile-datablock'">
< dt class="'profile-data'">< data:displayname/>< /dt>

< b:if cond="'data:showlocation">
< dd class="'profile-data'">< data:location/>< /dd>
< /b:if>

< b:if cond="'data:aboutme">< dd class="'profile-textblock'">< data:aboutme/>< /dd>< /b:if>
< /dl>
< a class="'profile-link'" href="'data:userUrl'">< data:viewprofilemsg/>< /a>
< /b:if>

< b:include name="'quickedit'/">
< /div>
< /b:includable>
< /b:widget>
< /b:section>
< /div>

< !-- spacer for skins that want sidebar and main to be the same height-->
< div class="'clear'"> < /div>

< /div> < !-- end content-wrapper -->

< div id="'footer-wrapper'">
< b:section class="'footer'" id="'footer'/">
< /div>

< /div>< /div> < !-- end outer-wrapper -->
< /body>
< /html>