1    #! /usr/bin/env python
       2    #
       3    # Safe ``poff''.
       4    #
       5    # Still, even 'lynx' and 'python' in ps_ignore_programs could use PPP!
       6    # Is there another way of seeing which commands are using PPP, rather
       7    # than using output from 'ps'?
       8    #
       9    # HOW ABOUT: ignore programs with PID lower than pppd.
      10    #
      11    # So-called "installation:"
      12    #  1. rename "/usr/bin/poff" to "/usr/bin/poff_drop_connection".
      13    #  2. make   "/usr/bin/poff"
      14    #     #!/bin/sh
      15    #     script=/home/cymbala/bin/rjc_poff.py
      16    #     echo "Using ${script}..."
      17    #     python ${script}   
      18    #    
      19    #
      20    # 'exp' = 'expected'
      21    # -----------------------------------------------------------------------------
      22    
      23    import re
      24    import os
      25    import popen2
      26    import string
      27    
      28    class my_program:
      29        def __init__(self):
      30            
      31            self.ps_syntax = 'ps awx'
      32            self.real_poff_command = 'poff_drop_connection'
      33            
      34            # Re. expressions
      35            self.re_spaces = re.compile('[ 	]{2,}')
      36            self.re_digits = re.compile('[0-9]+')
      37    
      38            # Stuff before COMMAND
      39            self.PID = '[0-9]+'
      40            self.TTY = '(\?|ttyS?[0-9]|pts/[0-9])'
      41            self.TTY_abstract = '(\?|ttyS?#|pts/#)'
      42            self.cc_PSC = '[DRSTZWNL]+'          # STAT: ``man ps'' (omitting '<')
      43            self.re_PSC = re.compile(' ' + self.cc_PSC + ' ')
      44            self.TIME = '[0-9]+[:][0-9]{2}'
      45            self.re_P_T_S_T = re.compile('^ *' + self.PID + ' ' + \
                                         self.TTY + ' +' + self.cc_PSC + \
                                         ' +' + self.TIME +' +')
      48            self.re_P_T_S_T_abstract = re.compile('^# ' + self.TTY_abstract + \
                                                  ' ' + 'PSC' +' #:# ')
      50            pass
      51    
      52        def ppp(self, interface):
      53            return_code = 0
      54            child_stdout = popen2.popen2('ifconfig ' + interface)
      55            data = child_stdout[0].read()
      56            if string.find(data, 'inet addr:') < 0:
      57                return_code = 1
      58            return return_code
      59    
      60        def abstract(self, sequence):
      61            # Truncate trailing newlines.
      62            # Delete leading spaces.
      63            # Change multiple white-space to single space.
      64            # Change any number of digits to '#'.
      65            # Change process state code to 'PSC'.
      66            #
      67    
      68            new_sequence = []
      69            for item in sequence:
      70                exp_original = item
      71                # SPACES
      72                while self.re_spaces.search(item):
      73                    matchobj = self.re_spaces.search(item)
      74                    item = item[:matchobj.start(0)] + ' ' + item[matchobj.end(0):]
      75                    pass
      76                # DIGITS
      77                while self.re_digits.search(item):
      78                    matchobj = self.re_digits.search(item)
      79                    item = item[:matchobj.start(0)] + '#' + item[matchobj.end(0):]
      80                    pass
      81                # NEWLINE
      82                if item[-1:] == '\n':
      83                    item = item[:-1]
      84                    pass
      85                if item[:1] == ' ':
      86                    item = item[1:]
      87                    pass
      88                # PROCESS STATE CODES
      89                while self.re_PSC.search(item):
      90                    matchobj = self.re_PSC.search(item)
      91                    item = item[:matchobj.start(0)] + ' PSC ' + \
                           item[matchobj.end(0):]
      93                    pass
      94    
      95                new_sequence.append(item)
      96                pass
      97            return new_sequence
      98    
      99        def examine_processes(self):
     100            # Expected lines from 'ps awx'.
     101            # Depends on computer set-up.
     102    
     103            # Criteria for ps_ignore_programs:
     104            #  Programs that can have variable arguments, such as lynx browser.
     105            #
     106            # Only criteria for elements in ps_exp_abstract:
     107            #  Do _not_ include commands that depend on PPP connection!
     108            #  For example,
     109            #   - exim
     110            #   - fetchmail
     111            #   - fetchnews
     112    
     113            # -------------------------------------------------------------------
     114            ps_ignore_programs = []


     117            #
     118            ps_ignore_programs.append('anacron')
     119            ps_ignore_programs.append('bash')
     120            ps_ignore_programs.append('bc')
     121            ps_ignore_programs.append('cron')
     122            ps_ignore_programs.append('CRON')
     123            ps_ignore_programs.append('emacs')
     124            ps_ignore_programs.append('floppybackup')
     125            ps_ignore_programs.append('getty')
     126            ps_ignore_programs.append('gzip')
     127            ps_ignore_programs.append('identd')
     128            ps_ignore_programs.append('ifconfig')   # used by this script!
     129            ps_ignore_programs.append('info')
     130            ps_ignore_programs.append('less')
     131            ps_ignore_programs.append('leafnode')  # fetchnews uses PPP.
     132            ps_ignore_programs.append('man')
     133            ps_ignore_programs.append('most')
     134            ps_ignore_programs.append('oleo')
     135            ps_ignore_programs.append('pager')
     136            ps_ignore_programs.append('poff')
     137            ps_ignore_programs.append('portmap')
     138            ps_ignore_programs.append('pppd')
     139            ps_ignore_programs.append('pump')
     140            ps_ignore_programs.append('python')
     141            ps_ignore_programs.append('rjc_bak.sh')
     142            ps_ignore_programs.append('run-parts')
     143            ps_ignore_programs.append('sendmail') # Command not in ps_exp_abstract:
     144            # ? PSC #:# /usr/sbin/sendmail -i -FCronDaemon -odi -oem root
     145            
     146            ps_ignore_programs.append('lockvc')
     147            ps_ignore_programs.append('sh')
     148            ps_ignore_programs.append('sleep')
     149            ps_ignore_programs.append('sshd')
     150            ps_ignore_programs.append('ssh-agent')
     151            ps_ignore_programs.append('tar')
     152            ps_ignore_programs.append('top')
     153            ps_ignore_programs.append('vi')
     154            ps_ignore_programs.append('wwwoffled')
     155            #
     156            # Perhaps truncate '/usr/bin' and '/usr/sbin' too.
     157            #
     158            # --------------------------------------------------------------------
     159            ps_exp_abstract = []
     160            # 'PSC' is any combination of process state codes (``man ps'').
     161            ps_exp_abstract.append('PID TTY    STAT   TIME COMMAND')
     162            ps_exp_abstract.append('# ?        PSC    #:# init [#]')
     163            ps_exp_abstract.append('# ?        PSC    #:# [kflushd]')
     164            ps_exp_abstract.append('# ?        PSC    #:# [kpiod]')
     165            ps_exp_abstract.append('# ?        PSC    #:# [kswapd]')
     166            ps_exp_abstract.append('# ?        PSC    #:# /sbin/klogd')
     167            ps_exp_abstract.append('# ?        PSC    #:# identd')
     168            ps_exp_abstract.append('# ?        PSC    #:# [klogd]')
     169            ps_exp_abstract.append('# ?        PSC    #:# [kswapd]')
     170            ps_exp_abstract.append('# ?        PSC    #:# [kupdate]')
     171            ps_exp_abstract.append('# ?        PSC    #:# [kupdate]')
     172            ps_exp_abstract.append('# ?        PSC    #:# [portmap]')
     173            ps_exp_abstract.append('# ?        PSC    #:# /sbin/syslogd')
     174            ps_exp_abstract.append('# ?        PSC    #:# [syslogd]')
     175            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/cron')
     176            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/inetd')
     177            ps_exp_abstract.append('# ?        PSC    #:# [atd]')
     178            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/atd')
     179            ps_exp_abstract.append('# ?        PSC    #:# [cardmgr]')
     180            ps_exp_abstract.append('# ?        PSC    #:# [gpm]')
     181            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/gpm -m /dev/psaux -t ps2 -Rms3')
     182            ps_exp_abstract.append('# ?        PSC    #:# [inetd]')
     183            ps_exp_abstract.append('# ?        PSC    #:# [ispell]')
     184            ps_exp_abstract.append('# ?        PSC    #:# [junkbuster]')
     185            ps_exp_abstract.append('# ?        PSC    #:# [lockd]')
     186            ps_exp_abstract.append('# ?        PSC    #:# [lpd]')
     187            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/lpd')
     188            ps_exp_abstract.append('# ?        PSC    #:# [rpc.statd]')
     189            ps_exp_abstract.append('# ?        PSC    #:# [rpciod]')
     190            ps_exp_abstract.append('# tty#     PSC    #:# -bash')
     191            ps_exp_abstract.append('# tty#     PSC    #:# emacs')
     192            ps_exp_abstract.append('# tty##    SW     #:# [lockvc]')
     193            ps_exp_abstract.append('# tty#     PSC    #:# [bash]')
     194            #   missing: M-x shell [bash] on pts/
     195            ps_exp_abstract.append('# tty#     PSC    #:# [getty]')
     196            ps_exp_abstract.append('# tty#     PSC    #:# ps awx')
     197            ps_exp_abstract.append('# ?        PSC    #:# ps awx')
     198            ps_exp_abstract.append('# ?        PSC    #:# /usr/bin/ispell -a -m -B')
     199            ps_exp_abstract.append('# tty#     PSC    #:# sh /usr/bin/poff')
     200            ps_exp_abstract.append('# ttyS#    PSC    #:# /usr/sbin/pppd call provider')
     201            ps_exp_abstract.append('# ?        PSC    #:# /usr/sbin/wwwoffled -c /etc/wwwoffle/wwwoffle.conf')
     202            # -------------------------------------------------------------------
     203    
     204            # Normalize expected list.
     205            ps_exp_abstract = self.abstract(ps_exp_abstract)
     206    
     207            # Get what's running
     208            commands = os.popen(self.ps_syntax)
     209            ps_actual = commands.readlines()
     210            commands.close()
     211            for command in ps_actual:
     212                #
     213                # Test expression against all commands.
     214                if not self.re_P_T_S_T.match(command):
     215                    if not command == '  PID TTY      STAT   TIME COMMAND\n':
     216                        print self.re_P_T_S_T.pattern
     217                        raise 'Not matched-' + command + '-'
     218                    pass
     219                pass
     220    
     221            # Get abstract representations.
     222            ps_actual = self.abstract(ps_actual)
     223    
     224            # If a running command is not in the expected list, abort.
     225            for command_abstract in ps_actual:
     226                #
     227                # Test expression using all command-abstracts.
     228                if not self.re_P_T_S_T_abstract.match(command_abstract):
     229                    if not command_abstract == 'PID TTY STAT TIME COMMAND':
     230                        print self.re_P_T_S_T_abstract.pattern
     231                        raise 'Not matched', command_abstract
     232                    pass
     233    
     234                if not command_abstract in ps_exp_abstract:
     235                    # Is it an 'ignore-program'?
     236                    
     237                    # Where does program information start?
     238                    matchobj = self.re_P_T_S_T_abstract.match(command_abstract)
     239                    # Assign executable.
     240                    executable = command_abstract[matchobj.end(0):]
     241                    # Discard everything after program name.
     242                    executable_space = string.find(executable, ' ')
     243                    if executable_space > -1:
     244                        executable = executable[:executable_space]
     245                        pass
     246                    # Remove surrounding brackets, if any.
     247                    executable_open_bracket = string.find(executable, '[')
     248                    if executable_open_bracket > -1:
     249                        executable = executable[executable_open_bracket+1:]
     250                        pass
     251                    #
     252                    # When closing ']' not after executable: '[lynx <defunct>]'
     253                    executable_open_bracket = string.find(executable, ']')
     254                    if executable_open_bracket > -1:
     255                        executable = executable[:-1]
     256                        pass
     257                    #
     258                    # Remove path.
     259                    executable = string.split(executable, '/')[-1]
     260                    #
     261                    if not executable in ps_ignore_programs:
     262                        print ps_ignore_programs
     263                        print executable
     264                        raise 'Command not in ps_exp_abstract', \
                              command_abstract
     266    
     267                    pass
     268                pass
     269    
     270            # As long as ps_exp_abstract does not contain an
     271            # entry that depends on PPP connection, it's okay to
     272            # use 'poff' to end PPP.
     273            #
     274            kill_ppp = os.popen(self.real_poff_command)
     275            kill_ppp.close()
     276            pass
     277        
     278    
     279        def __call__(self):
     280            interface = 'ppp0'
     281            if self.ppp(interface) == 0:
     282                self.examine_processes()
     283                pass
     284            else:
     285                print 'Interface not up: ' + interface
     286                pass
     287            pass
     288    
     289        pass
     290    # .......................................................
     291    main = my_program()
     292    main()
     293    
     294    ###
     295    #
     296    # Local variables:
     297    # py-indent-offset: 4
     298    # End: