Add the Power of Python to the DOS Preboot!
I can't count how many times I have been frustrated by DOS batch scripting. It reminds me of Monty Python's favorite joke about cutting a tree down with a herring. I have come to the conclusion that batch scripting just doesn't cut it anymore. Well, now we can have Python come to the rescue in DOS as well as Linux to save us from our scripting woes. In this article I will walk you through how to install Python for DOS in the Microsoft DOS preboot environment.
It might be worth mentioning that I have a previously written article about how to add the Python interpreter to the Linux preboot. Using these two articles you can construct for yourself a single programming environment for both DOS and Linux. If this isn't the Holy Grail of Deployment Solution programming, I don't know what is.
There are a few hurdles to jump over to make this work, the first being that DOS images don't have enough space (2.8 megabytes maximum) to hold the Python interpreter and libraries. The simplest way to solve this problem by running Python directly off the eXpress\Deployment Server share.
The second caveat is that that Python for DOS recommends that Python be run in a version of DOS that supports long file names, but our pre-boot environment doesn't. They have, however, provided mechanisms for it to work without long file name support, but you always have to remember to make all your filenames 8 characters or less. I grappled with this problem while writing this article, and this is the path I have chosen (coping without long filename support) because it proved very difficult to enable it. There are drivers out there that add long filename support for DOS, but they were either buggy, didn't support long filenames on network shares (almost always the case), or just didn't work well in the pre-boot. So you can try it if you like; perhaps you'll be more successful, but I seemed to get Python to do what I needed it to without it.
Therefore, ALWAYS remember to name all your Python files with short filenames no longer than 8 characters and always access files using their short file name equivalent.
I should mention here that this is in no way endorsed or supported by Altiris, and you do this at your own risk. When doing something like this always make sure to do it in a test environment first.
Ok, lets get started. Follow these steps to install the Python interpreter in your DOS preboot.
Download PythonD, a version of Python 2.4.2 compiled using the DJGPP by Delorie.
The general website is: http://www.caddit.net/pythond/.
This contains the Python interpreter compiled using DJGPP, as well as all the libraries (modules).
Create a folder named "pyDOS" on your desktop and unzip pyd24219.zip into it. Copy that folder to your "eXpress\Deployment Server\" share. This is what we will be accessing through the preboot.
In order for Python for DOS to run, it needs the cwsdpmi.exe executable. This is a virtual memory handler that programs compiled with the DJGPP compiler use. Download the csdpmi package from DJGPP from here.
Unzip that file to your desktop, pull out cwsdpmi.exe and place it in "eXpress\Deployment Server\pyDOS\BIN\" folder. It should be noted that "cwsdpmi.exe" requires a swap file in order to run properly. It will try to create it in the C:\ directory, and since we are in a preboot environment you may not have a disk mounted there. If it is unable to create a file, Python will still run, but you will run the risk of running out of memory very quickly. The best solution is to enable a ram drive for the DOS environment. This ram drive will appear as C:\ (if there already isn't a disk there of course).
To enable a ramdrive in the DOS preboot: open the PXE Configuration Utility, click on your DOS configuration, and then click "Edit", and then "Edit Boot Image".
Click on "config.sys" in the configuration editor and look for the line:
$$RemRAMDRIVE$$ DEVICEHIGH=\vdisk.sys /e 5760 512 512
Change this line to be as follows:
DEVICEHIGH=\vdisk.sys /e 33000 512 512
What we just did is enabled vdisk.sys to be called (that's what creates the ramdrive), and increased the default size from 5 megabytes to 32 megabytes.
Note: 32 Megabytes is the maximum size.
We now need to copy vdisk.sys into place. Right click your configuration (looks like a top level directory; it'll have "MenuOption" in it), then click "Add File". Browse to "eXpress\Deployment Server\Bootwiz\platforms\dos\Optional\Boot\vdisk.sys" and press "Open". You should now see vdisk.sys in the same directory as autoexec.bat and config.sys.
Now that you have the ramdisk enabled, cwsdpmi.exe can now create a swap file under C:\.
We now need to add a few lines to the autoexec.bat to set some environment variables needed for Python, as well as add the Python executable to the path. Click on autoexec.bat and find the line that begins with "set PATH=". You'll see a list following the equal sign separated by semicolons. Go to the end of the line and add:
This will add the Python binary to the path. Make sure not to miss the semicolon!
Now we need to add a few lines to the autoexec.bat for Python's sake. These lines tell Python where the libraries (modules) are found, as well as inform the interpreter that we are working without long file name support. Placing it right after the path declaration is probably as good as any (but it can't go at the beginning of the file because we are referencing ALTIRIS_SHARE which is declared near the top. Add the following lines.
set PYTHONHOME=%ALTIRIS_SHARE%:\pyDOS\BIN set PYTHONCASEOK=1
Press Next, then Next. Your DOS image should be regenerating. Once it is done click Finish, then OK, then SAVE (Very Important).
You should now see a little progress bar running. Go ahead and press OK (it is not necessary to wait for the progress bar to stop running).
Alright, Python is installed. Now let's start talking about Python scripting.
Since Python is a very high level language, most people will want to use a program, such as IDLE, to compose their code. Using this tool will help catch syntactical bugs in your programs, and also give you a Python interpreter shell to work with. Visit http://www.python.org and install a windows version of Python (which includes IDLE).
It is probably a good idea to create a folder on your eXpress\Deployment Server share to hold your Python scripts. In my example I will use eXpress\Deployment Server\pyScrip folder (remember, 8 characters or less). We will then call scripts within this folder using a DOS command.
How to set up your Scripts folder
Browse to your eXpress\Deployment Server\ folder and create a folder called "pyScrip". Then within that folder create a new file called pytest.py.
Open that file with your Python editor, or just notepad.exe and enter the following script.
File = open("F:\pyScrip\Hello.txt","w") File.writelines("Hello from the Python World") File.close()
Save and close the file.
Now go to your Deployment Console and create a new job calling it something like "Dos Python test". Go to Add->Run Script and enter the following script.
REM BOOTWORK UNLOAD python %ALTIRIS_SHARE%\pyScrip\pytest.py
This calls the script you just wrote using the Python interpreter. As way of explanation, the "REM BOOTWORK UNLOAD" line frees up more conventional memory in the pre-boot. ( While not absolutely necessary, it is important we don't run out of memory too quickly. )
Click on the "DOS" radio button towards the bottom of the window and then press "Next".
Click on "Automation pre-boot environment" and then change the field below it to be your DOS preboot environment (usually "DOS Managed (32-bit)").
Click Next and then Finish.
You're Done! Now test you job by dragging it on a machine. Watch to see for the Hello.txt file to appear within your eXpress\Deployment Server\pyScrip folder. If it does you have set up everything correctly!
You can now use Python in the place of batch scripting. However, if you still want to call DOS programs using the command line, here's an example of a very useful class that will run a command and then record its output to a logfile.
import os,string class Runner: def __init__(self,logfile): self.logfile = logfile def run(self,cmd): '''Runs a command putting the standard out in the logfile''' handle = os.popen(cmd, 'r') log = open(self.logfile, 'a') log.write("\n===========================================\n") log.write("Running Command: " + cmd + "\n") log.write("===========================================\n") log.write(string.join(handle.readlines())) handle.close() log.close()
You can also add more functions to log specific output, write a timestamp, etc.
To make debugging of scripts easier, you can route the standard output of your call to your script to a file on the eXpress share. For example, I can change the call to the Python interpreter to:
python %ALTIRIS_SHARE%\pyScrip\pytest.py > %ALTIRIS_SHARE%\pyScript\Debug.txt"
to see any errors that might be causing my script to fail.