#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Atlantic City, NJ
    Posts
    327
    Rep Power
    13

    Converting To Classes


    I've been reading a book on python and I just got to the classes section(I know I'm way behind). I understand the concept behind classes in terms of OOP and inheritance. I'm just a little lost on the design part.

    To try and learn how to do some OOP with classes I decided to try and take a script I wrote and make it with classes. The script is for my gentoo linux servers which checks for rootkits via chkrootkit, checks for package updates, checks for security updates, and mails the results to the root user of the server. Here is the script:

    Code:
    #!/usr/bin/env python
    
    import os, commands
    
    resultstmp = open('/tmp/results.txt', 'w')
    
    os.system('touch /tmp/chkroot.txt')
    chktmp = open('/tmp/chkroot.txt', 'r+')
    
    def header(headername):
    	"""Create the header files for 
    	each section"""
    	resultstmp.write("-" * 60 + "\n")
    	resultstmp.write("                   " + headername + "\n")
    	resultstmp.write("-" * 60 + "\n")
    	
    header("Python Security Check Script")
    resultstmp.write("\n")
    
    header("Chkrootkit Results")
    chktmp.write(commands.getoutput('chkrootkit'))
    
    bolin = 0
    for line in chktmp.readlines():
    	if "INFECTED" in line:
    		resultstmp.write(line)
    		bolin = 1
    if bolin == 0:
    	resultstmp.write("Nothing Infected...\n\n")
    
    #Sync portage
    os.system('emerge sync > /dev/null 2> /dev/null')
    
    header("Available Package Updates")
    resultstmp.write(commands.getoutput('emerge -up world | grep ebuild') + "\n\n")
    
    header("Possible Security Updates")
    resultstmp.write(commands.getoutput('glsa-check -ln 2> /dev/null') + "\n\n")
    
    date = commands.getoutput('date')
    hostname = commands.getoutput('hostname')
    
    resultstmp.close()
    chktmp.close()
    
    #Mail Everything
    os.system('cat /tmp/results.txt | mail -s "Security Report for %s on %s" security@localhost' % (hostname, date))
    
    #Clean up the temporary files
    os.system('rm -f /tmp/results.txt')
    os.system('rm -f /tmp/chkroot.txt')
    So, I've been trying to get this into classes for some time now. I think the real issue I'm having here is the layout. How would you split this up in terms of classes and methods? From the little I've read on classes, it seems that all the names should go in __init__. Will the names defined in __init__ be accessible by the other methods? What about when I make an instance of the class. Will the names in __init__ be accessible then?

    I'm sorry for the dumb questions but I'm new to all this.

    Thanks in advance.
    I'll learn this stuff someday.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2004
    Posts
    461
    Rep Power
    24
    that is what self is for. Every function that you have defined in the class should also ask for self you do this by just putting self in the ( ) of the function. self will have all the veribles that are in the class and that are defined to be global.

    you should also check some of the python documentaion on pythons site. that is how i learned. also look at tuts on google for python and classes.
  4. #3
  5. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    There's no one obvious way to turn this into a class since it depends on the features you want. Personally I would set up the files in the __init__ method and then use the other methods (actions) to do things with those files.

    You might want to look at the subprocess module in Python 2.4, which offers many improvements over the commands module; this could also replace the os.system() calls in your program.

    There are also a bunch of functions on the os module that you should look at i.e. remove(), which is used to delete a file. You can do this using the shell of course, but this way is more Pythonic and portable.

    You could replace the touch command with something like this:

    Code:
    Mark-Smiths-Computer:~ Mark$ cd Desktop/
    Mark-Smiths-Computer:~/Desktop Mark$ ls
    Mark-Smiths-Computer:~/Desktop Mark$ python2.4
    Python 2.4 (#1, Dec 12 2004, 22:12:51) 
    [GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> file('text', 'w')            
    <open file 'text', mode 'w' at 0x4a458>
    >>>
    Mark-Smiths-Computer:~/Desktop Mark$ ls
    text
    As you can see, this created a file in the current working directory. You can change this with os.chdir() .

    The shell is a very powerful tool and we all know we love it . The problems begin when a programmer who doesn't understand the shell tries to read your code.

    Using shell commands in your programs can cut down on a lot of work but it can drastically reduce readability and in some cases portability. This is probably not an issue for this but you might want to keep it in mind .

    Hope this helps,

    Mark.
    programming language development: www.netytan.com Hula

  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Atlantic City, NJ
    Posts
    327
    Rep Power
    13
    Thanks guys. Actually, Netyan, it was my understanding that opening a file with 'w' creates it if it does not already exist. For reasons unkown to me this did not work so I was forced to use the 'touch' command.

    So there is no way to put this into a class? No clear design if you split up each section into a seperate function?
    I'll learn this stuff someday.
  8. #5
  9. Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Dec 2004
    Location
    Meriden, Connecticut
    Posts
    1,797
    Rep Power
    154
    I'm not sure what you mean when you say using 'w' when opening a file didn't work. Did it not create the file? I'm sure you already know this, but it also re-writes existing files. Where you have,
    resultstmp = open('/tmp/results.txt', 'w')
    Every time you do resultstmp.write(), it re-writes that file, so you will only have the last thing you wrote in the file by the time your code is done. What you should do is check if the file exists, create it by using 'w' if it does not already exist. And then simply use 'a', to add things to the file, this will just add data onto the end of the file. It still needs '\n'.
  10. #6
  11. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    Originally Posted by Shiner_Man
    Thanks guys. Actually, Netyan, it was my understanding that opening a file with 'w' creates it if it does not already exist. For reasons unkown to me this did not work so I was forced to use the 'touch' command.

    So there is no way to put this into a class? No clear design if you split up each section into a seperate function?
    Mmmm, thats strange. I've used Python on several platforms and never had any problems creating files like this. If for some reason the file can not be created an IOError (or OSError) should be raised. What exactly are you seeing?

    You can write the a class to handle each of the different things you have to do i.e. you could write a class to interact with emerge since this seems to be the bulk of your program. You could write a mail class if you so desired using the smtplib module .

    http://www.python.org/doc/2.4/lib/module-smtplib.html

    It just really depends on what you are willing to do. At the moment the code works fine however it's not very reusable. By creating a class for emerge and a maybe function to send the email in a new module you could then use them in other programs .

    I wrote a useable email function as part an article here on DevShed that you might be interested in:

    http://www.devshed.com/c/a/Python/Python-on-the-Web/

    Think about your program and try and imagine what objects you would need to accomplish the task if you were somehow digitized and uploaded into your computer. Remember though, functions are good too .

    Note: functions are technically first member classes; instances of a class that implements the __call__ method among other things.

    Hope this helps,

    Mark.
    programming language development: www.netytan.com Hula

  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    78
    Rep Power
    10
    Should:
    Code:
    chktmp.write(commands.getoutput('chkrootkit'))
    be
    Code:
    resultstmp.write(commands.getoutput('chkrootkit'))
    or is that correct??

    I don't think that this really needs to use classes, just a slight reorder...

    --OH.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Atlantic City, NJ
    Posts
    327
    Rep Power
    13
    Netyan, I haven't revisited the issue with automatically creating the file. The only error it gave was that the file could not be found so I simply inserted the touch command. It is because I use the 'r+' attribute that the file does not automatically get created.

    I also looked into the smtp module but I failed to find a way to send a subject through it. It seems that this is a known issue. You know a way around it?

    Hydroxide, the reason I send the output into a seperate text file is so I can check for anything that is INFECTED and if so write that to the final results file. You did get me thinking though. Perhaps I don't need this text file at all. Couldn't I just get all the output from the 'chkrootkit' command and place it into a tuple. Then scan the tuple for the word INFECTED? I think that would make more sense.

    I guess what I want to do is write some portage classes. Something that would define methods that return things like available updates and security updates. I'll get working on that and let you guys know.

    Btw, anybody here use gentoo? I know some of you use linux but what distributions are you using?
    I'll learn this stuff someday.
  16. #9
  17. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    Originally Posted by Shiner_Man
    Netyan, I haven't revisited the issue with automatically creating the file. The only error it gave was that the file could not be found so I simply inserted the touch command. It is because I use the 'r+' attribute that the file does not automatically get created.

    I also looked into the smtp module but I failed to find a way to send a subject through it. It seems that this is a known issue. You know a way around it?

    Hydroxide, the reason I send the output into a seperate text file is so I can check for anything that is INFECTED and if so write that to the final results file. You did get me thinking though. Perhaps I don't need this text file at all. Couldn't I just get all the output from the 'chkrootkit' command and place it into a tuple. Then scan the tuple for the word INFECTED? I think that would make more sense.

    I guess what I want to do is write some portage classes. Something that would define methods that return things like available updates and security updates. I'll get working on that and let you guys know.

    Btw, anybody here use gentoo? I know some of you use linux but what distributions are you using?
    You can use the file('filename', 'w') in the place of touch. Because this isn't referenced anywhere it is removed from memory by the garbage collector, with the handy side effect of creating the file for you.

    You can then open the file again with the 'r+' flag. However like you said, you could probably get away without using the temp files at all .

    On the subject of smtplib you need to send the subject as part of the set of MIME headers - to send a subject you include the Subject: header.

    I'm running Mac OS X for the most port however I also have Suse Pro 9.0. I've thought about trying gentoo but don't know if I could be bothered to wait for everything to compile. Just to throw my own question in here, is this a major problem?

    Hope this helps,

    Mark.
    programming language development: www.netytan.com Hula

  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Atlantic City, NJ
    Posts
    327
    Rep Power
    13
    Could you elaborate a bit on the smtplib subject. Perhaps an example if you have time?

    If you have a relatively fast machine the compiling isn't that bad if you have patience. It has its pros and cons of course. You can also get a base system going in Gentoo with a stage3 install. This basically just unpacks the necessary precompiled binaries to get your system up and running. You would still have to emerge(install) whatever other applications you need like X, firefox, fluxbox, etc. This can take a while as well but you don't have to do it all at once of course.

    If you really want to learn a lot about linux I would suggest Gentoo since it does not "baby" you like some of the other distributions. It forces you to learn things which I think is very cool. Plus, python is the preferred Gentoo language. Its package management system called portage is written in python.
    I'll learn this stuff someday.
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    78
    Rep Power
    10
    How's this (untested):
    Code:
    #!/usr/bin/env python
    import os, commands
    
    class Results(list):
        def __init__(self):
            #Sync portage
            os.system('emerge sync > /dev/null 2> /dev/null')
            self.record(["Python Security Check Script",
                         ""])
            self.record(["Chkrootkit Results",
                         self.get_infected()])
            self.record(["Available Package Updates",
                         commands.getoutput('emerge -up world | grep ebuild')])
            self.record(["Possible Security Updates",
                         commands.getoutput('glsa-check -ln 2> /dev/null')])
    
        def get_infected(self):
            chktmp = [line for line in commands.getoutput('chkrootkit')
                      if "INFECTED" in line]
            if chktmp:
                return "\n".join(chktmp)
            else:
                return "Nothing Infected..."
    
        def record(self, item):
            headername, data = item
            self.append("-" * 60)
            self.append("                   " + headername)
            self.append("-" * 60)
            self.append(data)
            self.append("\n")
    
        def mail(self):
            txt = "\n".join(self)
            date = commands.getoutput('date')
            hostname = commands.getoutput('hostname')
            target = security@localhost
            # I think echo is correct - it's been a decade since I last used Unix
            os.system('echo %s | mail -s "Security Report for %s on %s" %s'%
               (txt,hostname, date, target))
            
    Results().mail()
    --OH.
  22. #12
  23. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    Originally Posted by Shiner_Man
    Could you elaborate a bit on the smtplib subject. Perhaps an example if you have time?

    If you have a relatively fast machine the compiling isn't that bad if you have patience. It has its pros and cons of course. You can also get a base system going in Gentoo with a stage3 install. This basically just unpacks the necessary precompiled binaries to get your system up and running. You would still have to emerge(install) whatever other applications you need like X, firefox, fluxbox, etc. This can take a while as well but you don't have to do it all at once of course.

    If you really want to learn a lot about linux I would suggest Gentoo since it does not "baby" you like some of the other distributions. It forces you to learn things which I think is very cool. Plus, python is the preferred Gentoo language. Its package management system called portage is written in python.
    Here's the mail() function that I wrote for the Python on the Web article. You can read more about this in the Devsheds Python section.

    Code:
    #!/usr/bin/env python
    
    import smtplib
    
    def mail(address, subject, message, host = 'localhost'):
    
        headers = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s'
        message = headers % (address[0], ','.join(address[1:]), subject, message)
    
        server = smtplib.SMTP(host)
        server.sendmail(address[0], address[1:], message)
        server.quit()
    
    if __name__ == '__main__':
        mail(('fromsomeone@somewhere.com', 'tosomeone@somewhere.com'), 'subject', 'message')
    Note: For whatever reason, the example in the article has had the backslashes removed from the MIME header so won't work .

    From experience smtp is a royal pain the in ***! Everything has to be just right or you won't get the email, and if the account isn't yours you won't even know about it . Once you have a working function though, you can use it over and over again with little modification .

    I like the example OH . One thought, you might want to replace os.system() and command.getoutput() calls with subprocess.call() a convenience function around the subprocess.Popen() class.

    Take care guys,

    Mark.
    programming language development: www.netytan.com Hula


IMN logo majestic logo threadwatch logo seochat tools logo