2

I made a script that takes a few arguments to run. Originally it could be run automatically with the argument 'auto', but I'm trying to daemonize it so it will run the run the script with the specified arguments as a daemon. The problem is that python-daemon and argparse don't seem to get along when it comes to deciding who parses what.

parser = argparse.ArgumentParser(usage='pyfilter.py <file> <options> <actions>')
parser.add_argument('file', help='blacklist file containing IPs', type=str)

subparsers = parser.add_subparsers(help='help', dest='action')

parser_update = subparsers.add_parser('update', help='update help')
parser_update.add_argument('-c', '--check', help="check IPs for abuse reports", dest="check", type=str, nargs=1)

parser_blacklist = subparsers.add_parser('blacklist', help='create iptables rules for malicious IPs specified'
                                                           'in the provided file')
parser_clear = subparsers.add_parser('clear', help='clear iptables')

parser_auto = subparsers.add_parser('auto', help='automatically run update and blacklist on a loop')
parser_auto.add_argument('-i', '--interval', help='specify the loop interval', dest='interval', type=int, nargs=1)
parser_auto.add_argument('-c', '--check', help="check IPs for abuse reports", dest="check", type=str, nargs=1)

parser.add_argument('-p', '--port', help='specify the port to block', type=int)
parser.add_argument('-v', '--verbose', help='write output to screen', nargs=1)
args = parser.parse_args()

. . .

class pyfilterDaemon():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path = '/tmp/pyfilter.pid'
        self.pidfile_timeout = 5

    def run(self):
        while True:
            update()
            blacklist()
            time.sleep(interval)
. . .

def main():
    #handle()
    d = pyfilterDaemon()
    daemon_runner = runner.DaemonRunner(d)
    daemon_runner.start()

Here's the commands I've tried to make this work:

root@tfof:~# ./pyfilter.py start
ERROR: File 'start' not found  # argparse parsed 'start' accidentally

root@tfof:~# ./pyfilter.py /etc/blacklist.lst -v yes auto
usage: checkfilter.py stop|restart|start  # now the argparse arguments are satisfied, but python-daemon is looking for its argument

root@tfof:~# ./pyfilter.py /etc/blacklist.lst -v yes auto start
usage: pyfilter.py <file> <options> <actions>
pyfilter.py: error: unrecognized arguments: start  # argparse is trying to parse 'start'

Would it be possible to pass off the 'start' argument to python-daemon or something? Or if I could just get rid of the argparsing it'd be fine, but the 'file' argument is a must.

  • `argparse` use `sys.argv[0]` as the `prog` name (for help), and parses the rest (as a default). But you can give it any list of strings. It is also good practice to package the `parser` in a function, and only do the `parse_args` call from a `if __name__` block. That way you control when and if it is used (in a script as opposed to an import). – hpaulj Nov 30 '17 at 18:10

1 Answers1

0

Argparse takes arguments from sys.argv per default (see here). It is not surprising that the behaviour you see here is happening, as you just call the parse_args function with the default arguments. You can just pass whatever you want to parse to it, instead of sys.argv.

See this question for an example.

So consume whatever you need for python-deamon and then parse the remaining args with argparse.

RunOrVeith
  • 4,487
  • 4
  • 32
  • 50
  • I've read the question and I think omitting the first argument would be a good solution, however I don't understand how it is done. At the top of my script I've put "argv = sys.argv[1:]" and then done "args = parser.parse_args(argv)", however it isn't working. Now argparse is recognizing the file argument that comes after 'start' as the action argument. I'm assuming this gave my argparsing a 1 offset for all of the arguments. Additionally, if start was specified (a daemon arg was specified) I'd like to be able to omit the requirement of an action argument, although that's a different question –  Nov 30 '17 at 16:07
  • I think you may just have to get sys.argv and adjust that according to your needs by hand, then feed whatever the result is to parse_args. – RunOrVeith Nov 30 '17 at 16:53