[Tutor] Watch and control access to an executable

Bill Burns billburns at pennswoods.net
Sat Apr 8 04:49:14 CEST 2006


Hi Tutors!

I have a problem that I've solved using Python, and I want to know if
I've done a good job :-)

Here's the problem:
At my work we have a Windows network. On a network drive lives a program
that a couple people need to access (there's a shortcut on each user's
Desktop to the executable). The problem is, only *one* person at a time
should run the program. For various reasons, it's possible for
information to get corrupted if two people are using the program at the
same time.

Here's the old solution to the problem:
Pick-up the phone.
Dial Dave's extension.
Dave this is Bill, I'm going to use the program.
Use the program.
Call Dave again and tell him I'm done using the program.
And Dave does the same for me.

Putting it mildly - this is a less than ideal way to run software ;-)

Now here's my solution:
1. Create a program let's call it 'myProg' that spawns 'otherProg'.
2. 'myProg' will utilize a config file.
3. When 'myProg' is started it looks in the config file to see if
'otherProg' is running.
4. If 'otherProg' is not running, start it and write to the config file
that 'otherProg' is running (also write who is running it).
5. 'myProg' continues to run on the user's computer, continuously
checking the PID (of 'otherProg') to see if the process is still alive.
6. When we don't find the PID anymore, write to the config file that
'otherProg' isn't running.
7. Shutdown 'myProg'.

Now in step #3 above - if 'myProg' reads the config file and finds that
the 'otherProg' is currently running, the user is warned that 'The
program is currently in use by <insert username from config file>!' And
'otherProg' is not started.

Couple of other things.....
1. I'm taking 'myProg' and creating a single-file executable using py2exe.
2. 'myProg.exe' and the config file live on the network in the same
directory as 'otherProg'.
3. User's have a shortcut on the Desktop to 'myProg.exe' instead of
'otherProg'.

BTW, I've never stopped to consider if there was a simple 'Windows
networking / permissions' type solution to the problem. I just went
straight to Python :-)

Here's my code. I'd appreciate any critiques!

Thanks,

Bill

<code>

import time
import os
import sys
import getpass
from ConfigParser import ConfigParser

import win32pdhutil
import win32con
import win32api

CONFIG_FILE = 'WatchProc.ini'

# Is there a better way to deal with this
# default config file data?
defaultConfigData = \
"""
[Process]
proc =

[Current User]
user =

[Executable]
exe =

[Process Status]
running =

[Shutdown Status]
ok =
"""

class WatchProc:
     def __init__(self):

         self.config = Config()
         self.user = getpass.getuser()
         self.checkConfig()

     def checkConfig(self):
         """
         Check the config file and see if a process is listed.
         If nothing is listed throw an error, else do
         checkStatus().
         """
         proc = self.config.getConfig('Process', 'proc')
         if proc == '':
             self.configFileError()
         else:
             self.checkStatus()

     def checkStatus(self):
         """
         Check the config file to see if the process is running
         or not. If running throw an error, else start the app.
         """
         status = self.config.getConfig('Process Status', 'running')
         if status == 'True': # App is in use.
             self.usageError()
         elif status == 'False': # App not in use.
             self.startApp()
         else: # Config file is not setup properly.
             self.configFileError()

     def startApp(self):
         """
         Write the user's name to the config file.
         Start the executable.
         Then monitor the process.
         """
         self.config.setConfig('Current User', 'user', self.user)
         self.startExe()
         time.sleep(1)
         self.monitorProcess()

     def monitorProcess(self):
         """
         Get the process name from the config file then continuously
         check to see if the process is running. When the process
         dies, call cleanup().
         """
         procname = self.config.getConfig('Process', 'proc')
         self.config.setConfig('Shutdown Status', 'ok', False)
         CHECK = True
         while CHECK:
             try:
                 pid = \
                 win32pdhutil.FindPerformanceAttributesByName(procname)
                 time.sleep(.5)
             except: # App has stopped running.
                 CHECK = False
         self.cleanup()

     def startExe(self):
         """
         Grab the name of the executable to start.
         Write to the config file that we're running.
         Spawn.
         """
         exe = self.config.getConfig('Executable', 'exe')
         self.config.setConfig('Process Status', 'running', True)
         os.spawnv(os.P_NOWAIT, exe, [])

     def cleanup(self):
         """
         Set the config file to the proper values (for a clean
         shutdown) and then exit.
         """
         self.config.setConfig('Shutdown Status', 'ok', True)
         self.config.setConfig('Process Status', 'running', False)
         self.config.setConfig('Current User', 'user', '')
         sys.exit()

     def configFileError(self):
         """
         Error message that gets called when a value in the config
         file is missing.
         """
         errMsg = 'The configuration file is not setup properly!'
         win32api.MessageBox(0,
                             errMsg,
                             'Config File Error',
                             win32con.MB_ICONEXCLAMATION)

     def usageError(self):
         """
         Error message that gets called when somebody else is
         currently running the "watched" program.
         """
         user = self.config.getConfig('Current User', 'user')
         errMsg = 'The program is currently in use by %s!' % (user)
         win32api.MessageBox(0,
                             errMsg,
                             'Start Program Error',
                             win32con.MB_ICONEXCLAMATION)

class Config:
     def __init__(self):
         # Check to see if the config file exists,
         # if not create one.
         if not os.path.exists(CONFIG_FILE):
             self.writeDefaultConfig()

         self.cp = ConfigParser()
         self.cp.read(CONFIG_FILE)

     def setConfig(self, section, option, value):
         """
         Method used to set config options and values
         in the ini file.
         """
         self.cp.set(section, option, value)
         f = open(CONFIG_FILE, 'w')
         self.cp.write(f)
         f.close()

     def getConfig(self, section, option):
         """
         Returns a value from the config file.
         """
         return self.cp.get(section, option)

     def writeDefaultConfig(self):
         """
         Writes the 'default' config file. Will only get
         called if one does not exist.
         """
         f = open(CONFIG_FILE, "w")
         f.write(defaultConfigData)
         f.close()

if __name__ == '__main__':
     watcher = WatchProc()

</code>









More information about the Tutor mailing list