Ensure that the three standard file descriptors (stdin, stdout and stderr)
[zeroinstall.git] / 0launch
blob51949d77afdd187d8ef238538a446bd8b08c38f4
1 #!/usr/bin/env python
2 import os, sys
3 from optparse import OptionParser
4 import logging
6 # Ensure stdin, stdout and stderr FDs exist, to avoid confusion
7 for std in (0, 1, 2):
8 try:
9 os.fstat(std)
10 except OSError:
11 fd = os.open('/dev/null', os.O_RDONLY)
12 if fd != std:
13 os.dup2(fd, std)
14 os.close(fd)
15 os.fstat(std)
17 parser = OptionParser(usage="usage: %prog [options] interface [args]\n"
18 " %prog --list [search-term]\n"
19 " %prog --import [signed-interface-files]\n"
20 " %prog --feed [interface]")
21 parser.add_option("-c", "--console", help="never use GUI", action='store_false', dest='gui')
22 parser.add_option("-d", "--download-only", help="fetch but don't run", action='store_true')
23 parser.add_option("-D", "--dry-run", help="just print actions", action='store_true')
24 parser.add_option("-f", "--feed", help="add or remove a feed", action='store_true')
25 parser.add_option("-g", "--gui", help="show graphical policy editor", action='store_true')
26 parser.add_option("-i", "--import", help="import from files, not from the network", action='store_true')
27 parser.add_option("-l", "--list", help="list all known interfaces", action='store_true')
28 parser.add_option("-m", "--main", help="name of the file to execute")
29 parser.add_option("-o", "--offline", help="try to avoid using the network", action='store_true')
30 parser.add_option("-r", "--refresh", help="refresh all used interfaces", action='store_true')
31 parser.add_option("-v", "--verbose", help="more verbose output", action='count')
32 parser.add_option("-V", "--version", help="display version information", action='store_true')
33 parser.disable_interspersed_args()
35 (options, args) = parser.parse_args()
37 if options.verbose:
38 logger = logging.getLogger()
39 if options.verbose == 1:
40 logger.setLevel(logging.INFO)
41 else:
42 logger.setLevel(logging.DEBUG)
44 from zeroinstall.injector import model, download, autopolicy, namespaces
46 if options.list:
47 if len(args) == 0:
48 match = None
49 elif len(args) == 1:
50 match = args[0].lower()
51 else:
52 parser.print_help()
53 sys.exit(1)
54 from zeroinstall.injector.iface_cache import iface_cache
55 for i in iface_cache.list_all_interfaces():
56 if match and match not in i.lower(): continue
57 print i
58 sys.exit(0)
60 if options.version:
61 import zeroinstall
62 print "0launch (zero-install) " + zeroinstall.version
63 print "Copyright (C) 2005 Thomas Leonard"
64 print "This program comes with ABSOLUTELY NO WARRANTY,"
65 print "to the extent permitted by law."
66 print "You may redistribute copies of this program"
67 print "under the terms of the GNU General Public License."
68 print "For more information about these matters, see the file named COPYING."
69 sys.exit(0)
71 if len(args) < 1:
72 if options.gui:
73 args = [namespaces.injector_gui_uri]
74 options.download_only = True
75 else:
76 parser.print_help()
77 sys.exit(1)
79 try:
80 if getattr(options, 'import'):
81 from zeroinstall.injector import gpg, handler
82 from zeroinstall.injector.iface_cache import iface_cache
83 from xml.dom import minidom
84 for x in args:
85 if not os.path.isfile(x):
86 raise model.SafeException("File '%s' does not exist" % x)
87 logging.info("Importing from file '%s'", x)
88 signed_data = file(x)
89 data, sigs = gpg.check_stream(signed_data)
90 doc = minidom.parseString(data.read())
91 uri = doc.documentElement.getAttribute('uri')
92 assert uri
93 iface = iface_cache.get_interface(uri)
94 logging.info("Importing information about interface %s", iface)
95 signed_data.seek(0)
96 iface_cache.check_signed_data(iface, signed_data, handler.Handler())
97 sys.exit(0)
99 if getattr(options, 'feed'):
100 from zeroinstall.injector import iface_cache, writer
101 from xml.dom import minidom
102 for x in args:
103 print "Feed '%s':\n" % x
104 x = model.canonical_iface_uri(x)
105 policy = autopolicy.AutoPolicy(x, download_only = True, dry_run = options.dry_run)
106 if options.offline:
107 policy.network_use = model.network_offline
108 policy.recalculate_with_dl()
109 interfaces = policy.get_feed_targets(policy.root)
110 for i in range(len(interfaces)):
111 feed = interfaces[i].get_feed(x)
112 if feed:
113 print "%d) Remove as feed for '%s'" % (i + 1, interfaces[i].uri)
114 else:
115 print "%d) Add as feed for '%s'" % (i + 1, interfaces[i].uri)
116 print
117 while True:
118 try:
119 i = raw_input('Enter a number, or CTRL-C to cancel [1]: ').strip()
120 except KeyboardInterrupt:
121 print
122 raise model.SafeException("Aborted at user request.")
123 if i == '':
124 i = 1
125 else:
126 try:
127 i = int(i)
128 except ValueError:
129 i = 0
130 if i > 0 and i <= len(interfaces):
131 break
132 print "Invalid number. Try again. (1 to %d)" % len(interfaces)
133 iface = interfaces[i - 1]
134 feed = iface.get_feed(x)
135 if feed:
136 iface.feeds.remove(feed)
137 else:
138 iface.feeds.append(model.Feed(x, arch = None, user_override = True))
139 writer.save_interface(iface)
140 print "\nFeed list for interface '%s' is now:" % iface.get_name()
141 if iface.feeds:
142 for f in iface.feeds:
143 print "- " + f.uri
144 else:
145 print "(no feeds)"
146 sys.exit(0)
148 iface_uri = model.canonical_iface_uri(args[0])
150 # Singleton instance used everywhere...
151 policy = autopolicy.AutoPolicy(iface_uri,
152 download_only = bool(options.download_only),
153 dry_run = options.dry_run)
155 if options.offline:
156 policy.network_use = model.network_offline
158 if options.gui is None and os.environ.get('DISPLAY', None):
159 if options.refresh:
160 options.gui = True
161 else:
162 options.gui = policy.need_download()
163 if options.gui:
164 # If we need to download anything, we might as well
165 # refresh all the interfaces first. Also, this triggers
166 # the 'checking for updates' box, which is non-interactive
167 # when there are no changes to the selection.
168 options.refresh = True
169 logging.info("Need to download; switching to GUI mode")
170 except model.SafeException, ex:
171 print >>sys.stderr, ex
172 sys.exit(1)
174 if options.gui:
175 policy.set_root(namespaces.injector_gui_uri)
177 # Try to start the GUI without using the network.
178 # The GUI can refresh itself if it wants to.
179 policy.freshness = 0
180 policy.network_use = model.network_minimal
182 prog_args = [iface_uri] + args[1:]
183 # Options apply to actual program, not GUI
184 if options.download_only:
185 policy.download_only = False
186 prog_args.insert(0, '--download-only')
187 if options.refresh:
188 options.refresh = False
189 prog_args.insert(0, '--refresh')
190 if options.main:
191 prog_args = ['--main', options.main] + prog_args
192 options.main = None
193 else:
194 prog_args = args[1:]
196 try:
197 policy.download_and_execute(prog_args, refresh = bool(options.refresh), main = options.main)
198 except autopolicy.NeedDownload, ex:
199 print ex
200 sys.exit(0)
201 except model.SafeException, ex:
202 print >>sys.stderr, ex
203 sys.exit(1)