import repository from arizona
[raven.git] / apps / stork / stork.py
1 #! /usr/bin/env python
2 """
3 <Program Name>
4    stork.py
5
6 <Started>
7    April 27, 2004
8
9 <Author>
10    Programmed by Justin Cappos.  Refactored by Jeffry Johnston.
11
12 <Purpose>
13    Install/remove package(s) and package dependencies.
14 """
15
16
17 #           [option, long option,    variable,     action,        data,     default,                            metavar, description]
18 """arizonaconfig
19    options=[
20             ["-C",   "--configfile", "configfile", "store",       "string", "/usr/local/stork/etc/stork.conf", "FILE",   "use a different config file (/usr/local/stork/etc/stork.conf is the default)"],
21             ["-s",   "--simulate",   "simulate",   "store_true",  None,     False,                              None,    "simulate the action"],
22             ["-R",   "--remove",     "remove",     "store_true",  None,     False,                              None,    "remove packages rather than install them"],
23             ["",     "--force",      "force",      "store_true",  None,     False,                              None,    "force removal and upgrade of packages"],
24             ["",     "--list",       "list",       "store_true",  None,     False,                              None,    "list all installed packages"],
25             ["",     "--dumpkeys",   "dumpkeys",   "store_true",  None,     False,                              None,    "dump the public key database to stdout"],
26             ["",     "--nowait",     "nowait",     "store_true",  None,     False,                              None,    "exits if another instance of stork is in progress"],\r
27             ["",     "--whatrequires","whatrequires","store_true",  None,     False,                              None,    "list all installed packages that require a dependency"],
28             ["",     "--lockdir",    "lockdir",    "store",       "string", "/var/lock",                        "dir",   "use the specified mutex lock directory (default /var/lock)"],
29             ["-U",   "--upgrade",    "upgrade",    "store_true",  None,     False,                              None,    "upgrade packages listed to the first version listed in the trustedpackages file"],
30             ["",     "--upgradeall", "upgradeall", "store_true",  None,     False,                              None,    "attempt to upgrade all packages"],
31             ["",     "--find",       "find",       "store_true",  None,     False,                              None,    "find a package"],
32             ["",     "--noupdateonstart","noupdateonstart","store_true", None, False,None, "do not try to download latest custom configuration files and keys from repository upon start of stork.py"],
33             ["",     "--tpdump",     "tpdump",     "store_true",  None,     False,                              None,    "dump trusted packages data"],
34             ["",     "--packagelogfile", "packagelogfile",     "store",        "string",       "/var/log/stork_package.log",  None,       "package log file"],
35             ["",     "--noinstall",  "noinstall",  "store_true",  None,     False,                              None,    "download, but do not install/update/remove anything"],
36             ["",     "--timeoutput", "timeoutput", "store_true",  None,     False,                              None,    "timestamp each line of output with time since start"],
37             ["",     "--heapoutput", "heapoutput", "store_true",  None,     False,                              None,    "heapstamp each line of output with current heap usage since start"]]
38    includes=[]
39 """
40
41 import os
42 import errno
43 import sys
44 import arizonaconfig
45 import arizonageneral
46 import storktrustedpackagesparse
47 import arizonareport
48 import arizonatransfer
49 import ravenlib.package.storkpackage
50 import ravenlib.package.transaction
51 import ravenlib.stats
52 import storkerror
53 import storkoutputfuncs
54 import storkpackagelist
55 import storkdependency
56 import storkversion
57 import storkstats
58 import storkusername
59 import time\r
60 import storkstatuscodes\r
61 import storkmutex\r
62 import storklog\r
63 \r
64 from storkexception import *\r
65
66
67
68 glo_tstart = time.time()
69 glo_counters = {}
70
71
72 def increment_counter(name, amount=1):
73     global glo_counters
74
75     if amount==0:
76        return
77
78     glo_counters[name] = glo_counters.get(name, 0) + amount
79
80
81
82
83
84 def format_counters():
85     global glo_counters
86
87     result = ""
88
89     for counter in glo_counters:
90         if result:
91             result = result + ", "
92         result = result + str(counter) + ": " + str(glo_counters[counter])
93
94     if result:
95         return "(" + result + ")"
96     else:
97         return ""
98
99
100
101
102
103 def exit(reason, exitstatus):
104
105    # set an exit code if something has already been done.
106    if (glo_counters.get("already installed", 0) > 0) or \
107       (glo_counters.get("already upgraded", 0) > 0):
108       exitstatus = exitstatus | storkstatuscodes.STATUS_ALREADY_DONE
109
110    if glo_counters.get("not found", 0) > 0:
111       exitstatus = exitstatus | storkstatuscodes.STATUS_NOT_FOUND
112
113    # we still want to return status '0' on success, so we only add other bits
114    # that indicate success (packages removed, packages installed) if we have
115    # some other condition than success.
116    if exitstatus != 0:
117       if glo_counters.get("installed", 0) > 0:
118          exitstatus = exitstatus | storkstatuscodes.STATUS_PACKAGES_INSTALLED
119       if glo_counters.get("removed", 0) > 0:
120          exitstatus = exitstatus | storkstatuscodes.STATUS_PACKAGES_REMOVED
121
122    try:
123        storklog.info("runtime: " + str(int(time.time()-glo_tstart)) + " seconds")
124        storklog.info("status: " + reason + " [" + str(exitstatus) + "]")
125    except:
126        # catch all errors. if something goes wrong, we want to still exit
127        # gracefully.
128        arizonareport.send_out(0, "exception while calling storklog")
129
130    arizonareport.send_out(2, "Stork completed in " + str(int(time.time()-glo_tstart)) + " seconds.")
131    arizonareport.send_out(0, reason + " [" + str(exitstatus) + "] " + str(format_counters()))
132    sys.exit(exitstatus)
133
134
135
136    
137 def do_dumpkeys(args):
138    storkusername.dump()
139
140
141
142
143
144 def do_tpdump(args):
145     if not args:
146         print "Package name argument is required for --tpdump"
147     for arg in args:
148         (name, ver, tags) = storkdependency.split_pack_name(arg)
149         pkgs = storkdependency.find_trusted_satisfying_packages(name, ver, tags, ignore_mantags=False)
150         for package in pkgs:
151             print "***** ", package["filename"]
152             if "tpentry" in package:
153                 print package["tpentry"]
154
155
156
157
158
159 def do_list(packlist):
160    """
161    <Purpose>
162       Lists all installed packages.
163
164    <Arguments>
165       None
166
167    <Returns>
168       None
169    """
170
171    if packlist:
172       packageList = ravenlib.package.storkpackage.get_installed_versions(packlist)
173    else:
174       packageList = ravenlib.package.storkpackage.get_installedpackages()
175
176    for pack in packageList:
177        print pack
178
179
180
181
182        
183 def do_upgradeall(packList):
184    """
185    <Purpose>
186       Upgrades all installed packages.
187
188    <Arguments>
189       None
190
191    <Returns>
192       None
193    """
194
195    packageList = ravenlib.package.storkpackage.get_installedpackages()
196
197    arizonaconfig.set_option("upgrade", True)
198
199    packNames = []
200    for pack in packageList:
201        if pack.find('-'):
202            pack = pack.split('-')[0]
203        packNames.append(pack)
204
205    packNames = arizonageneral.uniq(packNames)
206
207    do_install(packNames)
208
209
210
211
212
213 def do_whatrequires(deplist):
214    """
215    <Purpose>
216       Lists installed packages that require a dependency.
217       
218    <Arguments>
219       deplist:
220           A list of dependencies
221       
222    <Returns>
223       None
224    """
225    # check params
226    arizonageneral.check_type_stringlist(deplist, "deplist", "stork.do_whatrequires")
227
228    packageList = ravenlib.package.storkpackage.get_installedpackages_requiring(deplist)
229
230    for pack in packageList:
231        print pack
232
233
234
235 def do_remove(package_list):
236    """
237    <Purpose>
238       Removes (uninstalls) packages.
239
240    TODO fix comment
241    """
242    # check params
243    arizonageneral.check_type_stringlist(package_list, "package_list", "stork.do_remove")
244
245    transactions = ravenlib.package.transaction.tl_create()
246
247    for pack in package_list:
248        storklog.info("transaction: remove " + pack)
249        ravenlib.package.transaction.tl_remove(transactions, pack)
250
251    if arizonaconfig.get_option("force"):
252       arizonareport.send_error(0, "XXX --force not implemented for remove at this time");
253       sys.exit(-1)
254
255    if arizonaconfig.get_option("simulate"):
256       arizonareport.send_out(0, "\nWould perform: ")
257       ravenlib.package.transaction.tl_print(transactions)
258    else:
259       arizonareport.send_out(0, "\nTransaction List:")
260       ravenlib.package.transaction.tl_print(transactions)
261       try:
262          arizonareport.send_out(0, "\nExecuting:")
263          storklog.info("executing transactions")
264          ravenlib.package.storkpackage.execute(transactions)
265          packagelog(transactions)
266          increment_counter("removed", len(package_list))
267       except StorkException, e:
268          arizonareport.send_error(0, "\nAn error occurred while removing packages:\n" + str(e.msg))
269
270          success_trans = [x for x in transactions if x.get("status","None")=="success"]
271          success_names = [x["packname"] for x in success_trans]
272          fail_trans = [x for x in transactions if x.get("status","None")!="success"]
273          fail_names = [x["packname"] for x in fail_trans]
274
275          arizonareport.send_error(0, "Packages removed: " + ", ".join(success_names))
276          arizonareport.send_error(0, "Packages NOT removed: " + ", ".join(fail_names))
277          increment_counter("removed", len(success_trans))
278          increment_counter("not removed", len(fail_trans))
279
280          packagelog(transactions)
281          exit("Failure", 1)
282
283
284
285
286
287 def download_packages(download_list):
288    if arizonareport.get_verbosity() > 1:
289       width = arizonareport.console_size()[1]
290       if width == None:
291          width = 70
292    else:
293       width = 0
294
295    arizonatransfer.reset_transfer_method()
296
297    for pack in download_list:
298       # the pack better have been previously validated before we attempt to
299       # download it
300       assert(pack.get('_valid', False) == True)
301
302       pack_filename = pack['filename']
303
304       # get the file hash
305       pack_filehash = pack.get('hash', None)
306       if not pack_filehash:
307          arizonareport.send_error(0, "package " + str(pack) + " is missing file hash")
308          exit("Failure", 1);
309
310       # get the canonical hash. For packages without a user-specified URL, this
311       # is the same as pack['_metadatahash']. For packages with a user-specified
312       # URL, it is the hash with the 'URL' entry removed.
313       pack_metahash = storkpackagelist.package_metadata_dict_get_canonical_hash(pack)
314
315       # see if the package has already been downloaded (exists in
316       # /tmp) from a previous attempt.  Checks the hash to be sure.
317       filename = os.path.join("/tmp", pack_filename)
318       # does the file exist?
319       if os.path.isfile(filename):
320          # check its hash
321          metahash = ravenlib.package.storkpackage.get_package_metadata_hash(filename)
322          if metahash == pack_metahash:
323             # hash matched!  don't download this package
324             arizonareport.send_out(0, "\n   Already retrieved " + pack_filename + \
325                               " with hash " + pack_metahash)
326             pack['_localfilename'] = filename
327             continue
328
329       # pack['_URL'] is a list of possible URLs to retrieve the package, so
330       # we'll try each one until we find a successful result.
331       successful = False
332       for pack_url in pack['_URL']:
333          if not successful:
334             got_list = []
335             if arizonaconfig.get_option("simulate"):
336                arizonareport.send_out(0, "\n   Would retrieve " + pack_filename +
337                                     " with metahash=" + pack_metahash +
338                                     ", filehash=" + pack_filehash +
339                                     " from " +
340                                     pack_url)
341                pack['_localfilename'] = "/tmp/" + pack_metahash + "/" + pack_filename;
342                successful = True
343             else:
344                storklog.info("downloading " + pack_filename + " from " + pack_url)
345                arizonareport.send_out(0, "\n   Retrieving " + pack_filename +
346                                     " with metahash=" + pack_metahash +
347                                     ", filehash=" + pack_filehash +
348                                     " from " +
349                                     pack_url)
350
351                # split the URL into url_dir: everything leading up to the
352                # filename and url_fn: the filename.
353                (url_dir, url_fn) = os.path.split(pack_url)
354
355                file = {'filename' : url_fn,
356                        'hash' : pack_filehash,
357                        'hasfuncs' : [arizonatransfer.default_hashfunc]}
358
359                (successful, got_list) = arizonatransfer.getfiles1(url_dir,
360                                         [file], "/tmp", width)
361
362                if successful:
363                   pack['_localfilename'] = got_list[0]
364
365       # we were unable to download the package from any of the available URLs
366       if not successful:
367          # report errors and abort immediately
368          storklog.error("Unable to retrieve " + pack_filename)
369          arizonareport.send_error(0, "\n   Unable to retrieve " + pack_filename +
370                                    " with hash " + pack_metahash + " from " +
371                                    str(pack['_URL']))
372          arizonareport.send_error(0, "Aborting installation.")
373          exit("Failure", 1);
374
375
376
377 def format_packagelog_line(trans):
378    status_reason = trans.get("status-reason","")
379
380    # there shouldn't be case where there's both of these, but if there is then
381    # we'll leave it up to whomever reads the log to make sense of it
382    filename = trans.get("filename","")
383    packname = trans.get("packname","")
384
385    # if the status for a transaction item was never set (to "failure" or "success")
386    # then it must have been aborted
387
388    status = trans.get("status","aborted")
389
390    line = trans["op"] + " <" + status + ">"
391    if (filename != ""):
392       line = line + " " + filename
393    if (packname != ""):
394       line = line + " " + packname
395    if (status_reason != ""):
396       line = line + " (" + status_reason + ")"
397
398    return line
399
400 def packagelog(trans_list):
401    """
402    <Purpose>
403       Logs status information about packages. If the configuration option
404       'packagelogfile' is set, then information will be written to a file.
405       If 'log' is set then information will be written to syslog. If both
406       are set, then the log will be written to both places.
407
408    <Arguments>
409       None
410
411    <Returns>
412       None
413    """
414    if not trans_list:
415       return
416
417    # write it to the log file if that's what the user wants
418    fn = arizonaconfig.get_option("packagelogfile")
419    if fn:
420        try:
421            file = open(fn, "a")
422            if file:
423                for trans in trans_list:
424                    line = format_packagelog_line(trans)
425                    file.write(line + "\n")
426                file.close()
427        except IOError:
428            arizonareport.send_error(0, "failed to write package log: " + str(fn))
429
430    # write it to the stork log file, and (potentially) to the tempest log file
431    storklog.warn(line)
432
433
434 def do_find(package_list):
435    for pack in package_list:
436       (name, ver, tags) = storkdependency.split_pack_name(pack)
437
438       trusted_pkg_list = storkdependency.find_trusted_satisfying_packages(name, ver, tags)
439
440       for pkg in trusted_pkg_list:
441          print pkg['filename']
442          print "  url: ", pkg.get("_URL", None)
443
444 def do_install(package_list):
445    """
446    <Purpose>
447       Installs packages.  Installation is done in the following order (if
448       a step fails, the next steps will not be attempted):
449         1) resolve all package dependencies
450         2) download any packages that need to be downloaded
451         3) perform package removals
452         4) perform package installations
453         5) (TODO) remove temporary copies of installed packages
454
455    TODO fix comment
456    """
457    # check params
458    arizonageneral.check_type_stringlist(package_list, "package_list", "stork.do_install")
459
460    tStartDep = time.time()
461
462    # 1) resolve all dependencies
463    inst_list = []
464    for pack in package_list:
465       # check to see if the requested package and version level is already satisfied.
466       (sat_vers, unsat_vers) = storkdependency.get_installed_by_name(pack)
467
468       if arizonaconfig.get_option("upgrade"):
469           # upgrade mode, always attempt to install what the user wants
470           # regardless of whether or not it is already installed.
471           if (sat_vers or unsat_vers):
472               arizonareport.send_out(0, "Installed package(s) " + str(sat_vers+unsat_vers) +
473                                   " to be upgraded by " + pack)
474       else:
475           # install mode, don't install a package we already have
476           if sat_vers:
477               increment_counter("already installed");
478               arizonareport.send_out(0, "Installed package(s) " + str(sat_vers) +
479                                   " already satisfies the requirement " + pack)
480               continue
481           # install mode, don't install if we have something that conflicts
482           if unsat_vers:
483               increment_counter("already installed");
484               arizonareport.send_out(0, "Installed package(s) " + str(unsat_vers) +
485                                   " block the requirement " + pack)
486               arizonareport.send_out(0, "Use '--upgrade' to upgrade an existing package");
487               continue
488
489       (name, ver, tags) = storkdependency.split_pack_name(pack)
490
491       # get the dependencies necessary to satisfy this package
492       cur_list = storkdependency.satisfy(name, ver, tags, arizonaconfig.get_option("upgrade"))
493
494       if cur_list == None:
495          increment_counter("not found")
496       else:
497          inst_list += cur_list
498
499       arizonareport.send_out(2, "")
500
501    storkstats.report_time("depcheck_time", timeStart = tStartDep)
502
503    # at this point, inst_list contains a list of packages that we want to
504    # install. Any package that is an upgrade will contain a dictionary entry
505    # for '_upgrade_conflicts'
506
507    # look in the inst_list for packages that conflict with themselves; this
508    # indicates a package that is already installed and does not need upgrading.
509    new_inst_list = []
510    for pack in inst_list:
511        if '_upgrade_conflicts' in pack:
512           packname = pack['name'] + "-" + pack['version'] + "-" + pack['release']
513           if packname in pack['_upgrade_conflicts']:
514               storklog.info("already up-to-date: " + packname)
515               increment_counter("already upgraded");
516               arizonareport.send_out(2, "No need to upgrade '" + packname + "'")
517               continue
518        new_inst_list.append(pack)
519    inst_list = new_inst_list
520
521    # construct the list of packages to remove by looking at the packages to
522    # install and seeing what is marked as conflicting
523    conflict_list = []
524    for pack in inst_list:
525        if '_upgrade_conflicts' in pack:
526            conflict_list += pack['_upgrade_conflicts']
527
528    # are there any packages left to deal with? If not, then we are done.
529    if len(inst_list) == 0:
530       return
531
532    inst_list = arizonageneral.uniq(inst_list)
533
534    # make sure what we're planning to remove will not break other installed
535    # packages.
536    abort_broken_by_removal = False
537    if not arizonaconfig.get_option("force"):
538        for conflict in conflict_list:
539            broken_by_removal = storkdependency.get_reverse_dep(
540                                                   [conflict],
541                                                   conflict_list,
542                                                   inst_list)
543            if broken_by_removal:
544                storklog.error("removal of " + conflict + " would break package(s) " + str(broken_by_removal))
545                arizonareport.send_error(0, "\n   Removal of " + conflict +
546                                         " would break package(s) " + str(broken_by_removal))
547                abort_broken_by_removal=True
548                if abort_broken_by_removal:
549                   arizonareport.send_error(0, "Aborting installation.")
550                   sys.exit(1)
551
552    arizonareport.send_out_comma(0, "Downloading:")
553
554    # 2) download any packages that need to be downloaded
555    tStartDownload = time.time()
556    download_packages(inst_list)
557    storkstats.report_time("downloadpack_time", timeStart=tStartDownload)
558
559    tStartExecute = time.time()
560    transactions = ravenlib.package.transaction.tl_create()
561    for pack in inst_list:
562       if '_upgrade_conflicts' in pack:
563          storklog.info("transaction: upgrade " + pack['_localfilename'])
564          ravenlib.package.transaction.tl_upgrade(transactions,
565                                      pack['_localfilename'],
566                                      pack['_upgrade_conflicts'])
567       else:
568          storklog.info("transaction: install " + pack['_localfilename'])
569          ravenlib.package.transaction.tl_install(transactions,
570                                      pack['_localfilename'])
571
572    if arizonaconfig.get_option("simulate") or arizonaconfig.get_option("noinstall"):
573       arizonareport.send_out(0, "\nWould perform: ")
574       ravenlib.package.transaction.tl_print(transactions)
575    else:
576       arizonareport.send_out(0, "\nTransaction List:")
577       ravenlib.package.transaction.tl_print(transactions)
578
579       try:
580          arizonareport.send_out(0, "\nExecuting:")
581          storklog.info("executing transactions")
582          ravenlib.package.storkpackage.execute(transactions)
583
584          # update statistics
585          installed_filenames = ravenlib.package.transaction.tl_get_filename_list(transactions, ravenlib.package.transaction.INSTALL)
586          upgraded_filenames = ravenlib.package.transaction.tl_get_filename_list(transactions, ravenlib.package.transaction.UPGRADE)
587          increment_counter("installed", len(installed_filenames))
588          increment_counter("upgraded", len(upgraded_filenames))
589          packagelog(transactions)
590       except StorkException, e:
591          arizonareport.send_error(0, "\nAn error occurred while installing packages:\n" + str(e.msg))
592 \r
593          success_trans = [x for x in transactions if x.get("status","None")=="success"]\r
594          success_names = [x["filename"] for x in success_trans]
595          fail_trans = [x for x in transactions if x.get("status","None")!="success"]
596          fail_names = [x["filename"] for x in fail_trans]\r
597 \r
598          arizonareport.send_error(0, "Packages installed: " + ", ".join(success_names))\r
599          arizonareport.send_error(0, "Packages NOT installed: " + ", ".join(fail_names))\r
600          increment_counter("installed", len(success_trans))\r
601          increment_counter("not installed", len(fail_trans))\r
602 \r
603          packagelog(transactions)\r
604          exit("Failure", 1)\r
605
606    storkstats.report_time("execute_time", timeStart = tStartExecute)
607
608    # 5) remove temporary copies of installed packages
609    # TODO arizonaconfig.get_option("transfertempdir"), from arizonatransfer
610
611
612
613 def init(args):
614    """ TODO comment """
615    # check params
616    arizonageneral.check_type_stringlist(args, "args", "stork.init")
617
618    # check for root user
619    if os.geteuid() > 0:
620       arizonareport.send_error(0, "You must be root to run this program...")
621       exit("Failure", storkstatuscodes.STATUS_ERROR)
622 \r
623    # set up package list
624    # note that this may have been already initialized by arizonacurl
625    storkpackagelist.init()
626
627    # set up trustedpackages file
628    if not arizonaconfig.get_option("disable_tpf"):
629       storktrustedpackagesparse.init()
630
631    # look up each "package name" in the package list.  the user may have
632    # given a filename by mistake, so we want to convert filenames to
633    # real package names, if possible.
634    new_args = []
635    for name in args:
636       # the user may have used a relational operator (=,>,etc) in the package
637       # name. We'll check both the full name, and the name without the relop.
638       (name_only, ver, tags) = storkdependency.split_pack_name(name)
639
640       if (not storkpackagelist.package_exists(name)) and  \
641             (not storkpackagelist.package_exists(name_only)):
642          arizonareport.send_error(2, "Warning: A package named `" + name + \
643                                      "' does not exist.\n         Attempting filename match.")
644          try:
645             packname = storkpackagelist.find_package_name(name)
646          except TypeError, e:
647             arizonareport.send_error(0, str(e))
648             sys.exit(1)
649
650          if packname != None:
651             # found a match
652             arizonareport.send_error(2, "Warning: Using package name `" + \
653                                         packname + "' instead of `" + \
654                                         name + "'.\n")
655             new_args.append(packname)
656          else:
657             # couldn't find anything, but try the original name anyway
658             new_args.append(name)
659       else:
660          new_args.append(name)
661
662    return new_args
663
664
665
666
667
668 ########################### MAIN ###############################
669 def main(args):
670    """ TODO comment """
671
672    global glo_exitstatus;
673
674    # Initialize statistics module, setting start time to now. This will
675    # skip time spent outside of main(), such as time spent waiting for the
676    # mutex.
677    storkstats.init()
678
679    storklog.init()
680
681    # Record the elapsed time that it took us to get to this point. This will
682    # mainly reflect mutex wait time.
683    storkstats.report_time("mainstart_time", timeStart=glo_tstart)
684
685    # check params
686    arizonageneral.check_type_stringlist(args, "args", "stork.main")
687
688    # process command line and initialize variables
689    args = init(args)
690
691    # build up a list of operations the user has specified. The user is only
692    # supposed to ask for one of these, so if he wants more than one, we
693    # can complain.
694    mode = []
695    if arizonaconfig.get_option("remove"):
696       mode.append("remove")
697       op = do_remove
698    if arizonaconfig.get_option("whatrequires"):
699       mode.append("whatrequires")
700       op = do_whatrequires
701    if arizonaconfig.get_option("list"):
702       mode.append("list")
703       op = do_list
704    if arizonaconfig.get_option("tpdump"):
705       mode.append("tpdump")
706       op = do_tpdump
707    if arizonaconfig.get_option("upgrade"):
708       mode.append("upgrade")
709       op = do_install
710    if arizonaconfig.get_option("upgradeall"):
711       mode.append("upgradeall")
712       op = do_upgradeall
713    if arizonaconfig.get_option("dumpkeys"):
714       mode.append("dumpkeys")
715       op = do_dumpkeys
716    if arizonaconfig.get_option("find"):
717       mode.append("find")
718       op = do_find
719
720    # if the user didn't say what he wanted, then lets assume he wants
721    # to install
722    if not mode:
723       mode.append("install")
724       op = do_install
725
726    # complain if the user asked for more than one thing
727    if len(mode)>1:
728       arizonareport.send_error(0, "ERROR: you may use only one of " +
729                                   ",".join(mode))
730       exit("Failure", 1)
731
732    storklog.critical("operation: " + mode[0] + " " + " ".join(args))
733
734    # now perform the operation
735    op(args)
736
737    # Record time it took us to run. Name the statistic after what operation
738    # we did (i.e. stork_upgrade, stork_upgradeall, stork_list, etc)
739    storkstats.report_time(str(mode[0]))
740
741    exit("Success", 0)
742
743
744
745
746
747
748 # Start main
749 if __name__ == "__main__":
750    try:
751       # use error reporting tool
752       storkerror.init_error_reporting("stork.py")
753
754       # get command line and config file options
755       args = arizonaconfig.init_options("stork.py", configfile_optvar="configfile", version=storkversion.VERREL)
756
757       if arizonaconfig.get_option("timeoutput"):
758           storkoutputfuncs.enable_timestamp_output()
759
760       if arizonaconfig.get_option("heapoutput"):
761           storkoutputfuncs.enable_heapstamp_output()
762
763       if os.geteuid() > 0:
764           arizonareport.send_error(0, "You must be root to run this program...")
765           sys.exit(1)
766
767       # smbaker: wait for mutex before doing arizonacurl update, to prevent
768       # multiple storks from downloading keys/config files
769       storkmutex.wait_for_mutex()
770
771       main(args)
772    except KeyboardInterrupt:
773       arizonareport.send_out(0, "Exiting via keyboard interrupt...")
774       sys.exit(0)