# Copyright (C) 2008 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # import sys import shlex import getopt from utils import * from userprefs import Options, UserPrefs from vars import Vars from ui import cmd_exit from msg import * from levels import Levels def load_rc(rcfile): try: f = open(rcfile) except: return save_stdin = sys.stdin sys.stdin = f while True: inp = multi_input() if inp == None: break try: parse_line(levels,shlex.split(inp)) except ValueError, msg: common_err(msg) f.close() sys.stdin = save_stdin def multi_input(prompt = ''): """ Get input from user Allow multiple lines using a continuation character """ line = [] while True: try: text = raw_input(prompt) except EOFError: return None err_buf.incr_lineno() if options.regression_tests: print ".INP:",text sys.stdout.flush() sys.stderr.flush() stripped = text.strip() if stripped.endswith('\\'): stripped = stripped.rstrip('\\') line.append(stripped) if prompt: prompt = '> ' else: line.append(stripped) break return ''.join(line) def check_args(args,argsdim): if not argsdim: return True if len(argsdim) == 1: minargs = argsdim[0] return len(args) >= minargs else: minargs,maxargs = argsdim return len(args) >= minargs and len(args) <= maxargs # # Note on parsing # # Parsing tables are python dictionaries. # # Keywords are used as keys and the corresponding values are # lists (actually tuples, since they should be read-only) or # classes. In the former case, the keyword is a terminal and # in the latter, a new object for the class is created. The class # must have the cmd_table variable. # # The list has the following content: # # function: a function to handle this command # numargs_list: number of minimum/maximum arguments; for example, # (0,1) means one optional argument, (1,1) one required; if the # list is empty then the function will parse arguments itself # required minimum skill level: operator, administrator, expert # (encoded as a small integer from 0 to 2) # can the command cause transition to start (0 or 1) # used to check whether to wait4dc to end the transition # def show_usage(cmd): p = None try: p = cmd.__doc__ except: pass if p: print >> sys.stderr, p else: syntax_err(cmd.__name__) def parse_line(lvl,s): if not s: return True if s[0].startswith('#'): return True lvl.mark() pt = lvl.parse_root cmd = None i = 0 for i in range(len(s)): token = s[i] if token in pt: if type(pt[token]) == type(object): # on entering new level we need to set the # interactive option _before_ creating the level if not options.interactive and i == len(s)-1: set_interactive() lvl.new_level(pt[token],token) pt = lvl.parse_root # move to the next level else: cmd = pt[token] # terminal symbol break # and stop parsing else: syntax_err(s[i:]) lvl.release() return False if cmd: # found a terminal symbol if not user_prefs.check_skill_level(cmd[2]): lvl.release() skill_err(s[i]) return False args = s[i+1:] if not check_args(args,cmd[1]): lvl.release() show_usage(cmd[0]) return False args = s[i:] d = lambda: cmd[0](*args) rv = d() # execute the command # should we wait till the command takes effect? if user_prefs.get_wait() and rv != False and cmd[3] == 1: if not wait4dc(token, not options.batch): rv = False lvl.release() return rv != False return True def prereqs(): proglist = "which cibadmin crm_resource crm_attribute crm_mon" for prog in proglist.split(): if not is_program(prog): print >> sys.stderr, "%s not available, check your installation"%prog sys.exit(1) # three modes: interactive (no args supplied), batch (input from # a file), half-interactive (args supplied, but not batch) def cib_prompt(): return vars.cib_in_use or "live" def usage(rc): f = sys.stderr if rc == 0: f = sys.stdout print >> f, """ usage: crm [-D display_type] [-f file] [-hF] [args] Use crm without arguments for an interactive session. Supply one or more arguments for a "single-shot" use. Specify with -f a file which contains a script. Use '-' for standard input or use pipe/redirection. crm displays cli format configurations using a color scheme and/or in uppercase. Pick one of "color" or "uppercase", or use "-D color,uppercase" if you want colorful uppercase. Get plain output by "-D plain". The default may be set in user preferences (options). -F stands for force, if set all operations will behave as if force was specified on the line (e.g. configure commit). Examples: # crm -f stopapp2.cli # crm < stopapp2.cli # crm resource stop global_www # crm status """ sys.exit(rc) user_prefs = UserPrefs.getInstance() options = Options.getInstance() err_buf = ErrorBuffer.getInstance() vars = Vars.getInstance() levels = Levels.getInstance() # prefer the user set PATH os.putenv("PATH", "%s:%s" % (os.getenv("PATH"),vars.crm_daemon_dir)) def set_interactive(): '''Set the interactive option only if we're on a tty.''' if sys.stdin.isatty(): options.interactive = True def run(): prereqs() inp_file = '' load_rc(vars.rc_file) if not sys.stdin.isatty(): err_buf.reset_lineno() options.batch = True else: options.interactive = True try: opts, args = getopt.getopt(sys.argv[1:], \ 'whdf:FRD:', ("wait","version","help","debug","file=",\ "force","regression-tests","display=")) for o,p in opts: if o in ("-h","--help"): usage(0) elif o in ("--version"): print >> sys.stdout,("""%s Written by Dejan Muhamedagic """ % vars.crm_version) sys.exit(0) elif o == "-d": user_prefs.set_debug() elif o == "-R": options.regression_tests = True elif o in ("-D","--display"): user_prefs.set_output(p) elif o in ("-F","--force"): user_prefs.set_force() elif o in ("-f","--file"): options.batch = True options.interactive = False err_buf.reset_lineno() inp_file = p elif o in ("-w","--wait"): user_prefs.wait = "yes" except getopt.GetoptError,msg: print msg usage(1) # this special case is silly, but we have to keep it to # preserve the backward compatibility if len(args) == 1 and args[0].startswith("conf"): parse_line(levels,["configure"]) elif len(args) > 0: err_buf.reset_lineno() # we're not sure yet whether it's an interactive session or not # (single-shot commands aren't) options.interactive = False if parse_line(levels,shlex.split(' '.join(args))): # if the user entered a level, then just continue if not levels.previous(): sys.exit(0) else: sys.exit(1) if inp_file == "-": pass elif inp_file: try: f = open(inp_file) except IOError, msg: common_err(msg) usage(2) sys.stdin = f if options.interactive and not options.batch: from completion import setup_readline setup_readline() rc = 0 while True: if options.interactive and not options.batch: vars.prompt = "crm(%s)%s# " % (cib_prompt(),levels.getprompt()) inp = multi_input(vars.prompt) if inp == None: if options.interactive: cmd_exit("eof") else: cmd_exit("eof", rc) try: if not parse_line(levels,shlex.split(inp)): rc = 1 except ValueError, msg: rc = 1 common_err(msg) # vim:ts=4:sw=4:et: