2 import sys, os, subprocess, signal
6 exepath = os.path.split(exe)[0] or '.'
8 # fix the PYTHONPATH to include our lib dir
9 libpath = os.path.join(exepath, 'lib')
10 cmdpath = os.path.join(exepath, 'cmd')
11 sys.path[:0] = [libpath]
12 os.environ['PYTHONPATH'] = libpath + ':' + os.environ.get('PYTHONPATH', '')
13 os.environ['BUP_MAIN_EXE'] = os.path.abspath(exe)
15 from bup.helpers import *
18 def columnate(l, prefix):
20 clen = max(len(s) for s in l)
21 ncols = (78 - len(prefix)) / (clen + 2)
29 for s in range(0, len(l), rows):
30 cols.append(l[s:s+rows])
32 for row in zip(*cols):
33 out += prefix + ''.join(('%-*s' % (clen+2, s)) for s in row) + '\n'
38 log('Usage: bup <command> <options...>\n\n')
40 ftp = 'Browse backup sets using an ftp-like client',
41 fsck = 'Check backup sets for damage and add redundancy information',
42 fuse = 'Mount your backup sets as a filesystem',
43 help = 'Print detailed help for the given command',
44 index = 'Create or display the index of files to back up',
45 join = 'Retrieve a file backed up using "bup split"',
46 ls = 'Browse the files in your backup sets',
47 midx = 'Index objects to speed up future backups',
48 save = 'Save files into a backup set (note: run "bup index" first)',
49 split = 'Split a single file into its own backup set',
52 log('Common commands:\n')
53 for cmd,synopsis in sorted(common.items()):
54 log(' %-10s %s\n' % (cmd, synopsis))
57 log('Other available commands:\n')
59 for c in sorted(os.listdir(cmdpath) + os.listdir(exepath)):
60 if c.startswith('bup-') and c.find('.') < 0:
62 if cname not in common:
64 log(columnate(cmds, ' '))
67 log("See 'bup help <command>' for more information on " +
68 "a specific command.\n")
72 if len(argv) < 2 or not argv[1] or argv[1][0] == '-':
78 sp = os.path.join(exepath, 'bup-%s' % s)
79 if not os.path.exists(sp):
80 sp = os.path.join(cmdpath, 'bup-%s' % s)
83 if not os.path.exists(subpath(subcmd)):
84 log('error: unknown command "%s"\n' % subcmd)
88 already_fixed = atoi(os.environ.get('BUP_FORCE_TTY'))
89 if subcmd in ['ftp', 'help']:
91 fix_stdout = not already_fixed and os.isatty(1)
92 fix_stderr = not already_fixed and os.isatty(2)
95 if fix_stdout or fix_stderr:
96 amt = (fix_stdout and 1 or 0) + (fix_stderr and 2 or 0)
97 os.environ['BUP_FORCE_TTY'] = str(amt)
98 os.setsid() # make sure ctrl-c is sent just to us, not to child too
100 if fix_stdout or fix_stderr:
101 realf = fix_stderr and 2 or 1
102 drealf = os.dup(realf) # Popen goes crazy with stdout=2
103 n = subprocess.Popen([subpath('newliner')],
104 stdin=subprocess.PIPE, stdout=drealf,
105 close_fds=True, preexec_fn=force_tty)
107 outf = fix_stdout and n.stdin.fileno() or None
108 errf = fix_stderr and n.stdin.fileno() or None
115 class SigException(Exception):
116 def __init__(self, signum):
118 Exception.__init__(self, 'signal %d received' % signum)
119 def handler(signum, frame):
120 raise SigException(signum)
122 signal.signal(signal.SIGTERM, handler)
123 signal.signal(signal.SIGINT, handler)
129 p = subprocess.Popen([subpath(subcmd)] + argv[2:],
130 stdout=outf, stderr=errf, preexec_fn=force_tty)
132 # if we get a signal while waiting, we have to keep waiting, just
133 # in case our child doesn't die.
137 except SigException, e:
138 log('\nbup: %s\n' % e)
139 os.kill(p.pid, e.signum)
142 log('%s: %s\n' % (subpath(subcmd), e))
145 if p and p.poll() == None:
146 os.kill(p.pid, signal.SIGTERM)