Print this page
305 http_proxy value needs more checking for valid url syntax
2341 Client should be more conservative about closing sockets
4235 Misleading "node name" unknown messages when http_proxy set incorrectly
4495 Want ability to cancel individual file downloads
8902 Need a transport that downloads files by GET
9508 Captive portal test only run during catalog refresh
9613 Implicit refresh should raise InvalidDepotResponseException
9615 EOL clientside search v0
9629 EOL clientside support for filelist
9630 Hostile depot should live with other depot code
9631 HTTPS transport should be more rigorous in verification
9670 More specific error exceptions requested from search
9686 network operations should use accept-encoding when appropriate
9715 The info() operation should use the activity_lock


 111 
 112 class LogSink(object):
 113         """This is a dummy object that we can use to discard log entries
 114         without relying on non-portable interfaces such as /dev/null."""
 115 
 116         def write(self, *args, **kwargs):
 117                 """Discard the bits."""
 118                 pass
 119 
 120         def flush(self, *args, **kwargs):
 121                 """Discard the bits."""
 122                 pass
 123 
 124 def usage(text):
 125         if text:
 126                 emsg(text)
 127 
 128         print """\
 129 Usage: /usr/lib/pkg.depotd [-d repo_dir] [-p port] [-s threads]
 130            [-t socket_timeout] [--cfg-file] [--content-root] [--debug]
 131            [--log-access dest] [--log-errors dest] [--mirror] [--proxy-base url]
 132            [--readonly] [--rebuild] [--ssl-cert-file] [--ssl-dialog]
 133            [--ssl-key-file] [--writable-root dir]
 134 
 135         --cfg-file      The pathname of the file from which to read and to
 136                         write configuration information.
 137         --content-root  The file system path to the directory containing the
 138                         the static and other web content used by the depot's
 139                         browser user interface.  The default value is
 140                         '/usr/share/lib/pkg'.
 141         --debug         The name of a debug feature to enable; or a whitespace
 142                         or comma separated list of features to enable.  Possible
 143                         values are: headers.
 144         --log-access    The destination for any access related information
 145                         logged by the depot process.  Possible values are:
 146                         stderr, stdout, none, or an absolute pathname.  The
 147                         default value is stdout if stdout is a tty; otherwise
 148                         the default value is none.
 149         --log-errors    The destination for any errors or other information
 150                         logged by the depot process.  Possible values are:
 151                         stderr, stdout, none, or an absolute pathname.  The
 152                         default value is stderr.
 153         --mirror        Package mirror mode; publishing and metadata operations
 154                         disallowed.  Cannot be used with --readonly or
 155                         --rebuild.





 156         --proxy-base    The url to use as the base for generating internal
 157                         redirects and content.
 158         --readonly      Read-only operation; modifying operations disallowed.
 159                         Cannot be used with --mirror or --rebuild.
 160         --rebuild       Re-build the catalog from pkgs in depot.  Cannot be
 161                         used with --mirror or --readonly.
 162         --ssl-cert-file The absolute pathname to a PEM-encoded Certificate file.
 163                         This option must be used with --ssl-key-file.  Usage of
 164                         this option will cause the depot to only respond to SSL
 165                         requests on the provided port.
 166         --ssl-dialog    Specifies what method should be used to obtain the
 167                         passphrase needed to decrypt the file specified by
 168                         --ssl-key-file.  Supported values are: builtin,
 169                         exec:/path/to/program, or smf:fmri.  The default value
 170                         is builtin.
 171         --ssl-key-file  The absolute pathname to a PEM-encoded Private Key file.
 172                         This option must be used with --ssl-cert-file.  Usage of
 173                         this option will cause the depot to only respond to SSL
 174                         requests on the provided port.
 175         --writable-root The path to a directory to which the program has write


 185         def __init__(self, *args):
 186                 Exception.__init__(self, *args)
 187 
 188 if __name__ == "__main__":
 189 
 190         setlocale(locale.LC_ALL, "")
 191         gettext.install("pkg", "/usr/share/locale")
 192 
 193         debug_features = {
 194             "headers": False,
 195         }
 196         port = PORT_DEFAULT
 197         port_provided = False
 198         threads = THREADS_DEFAULT
 199         socket_timeout = SOCKET_TIMEOUT_DEFAULT
 200         readonly = READONLY_DEFAULT
 201         rebuild = REBUILD_DEFAULT
 202         reindex = REINDEX_DEFAULT
 203         proxy_base = None
 204         mirror = MIRROR_DEFAULT


 205         repo_config_file = None
 206         ssl_cert_file = None
 207         ssl_key_file = None
 208         ssl_dialog = "builtin"
 209         writable_root = None
 210 
 211         if "PKG_REPO" in os.environ:
 212                 repo_path = os.environ["PKG_REPO"]
 213         else:
 214                 repo_path = REPO_PATH_DEFAULT
 215 
 216         try:
 217                 content_root = os.environ["PKG_DEPOT_CONTENT"]
 218         except KeyError:
 219                 try:
 220                         content_root = os.path.join(os.environ['PKG_HOME'],
 221                             'share/lib/pkg')
 222                 except KeyError:
 223                         content_root = CONTENT_PATH_DEFAULT
 224 
 225         # By default, if the destination for a particular log type is not
 226         # specified, this is where we will send the output.
 227         log_routes = {
 228             "access": "none",
 229             "errors": "stderr"
 230         }
 231         log_opts = ["--log-%s" % log_type for log_type in log_routes]
 232 
 233         # If stdout is a tty, then send access output there by default instead
 234         # of discarding it.
 235         if os.isatty(sys.stdout.fileno()):
 236                 log_routes["access"] = "stdout"
 237 
 238         opt = None
 239         try:
 240                 long_opts = ["cfg-file=", "content-root=", "debug=", "mirror",
 241                     "proxy-base=", "readonly", "rebuild", "refresh-index",
 242                     "ssl-cert-file=", "ssl-dialog=", "ssl-key-file=",
 243                     "writable-root="]
 244                 for opt in log_opts:
 245                         long_opts.append("%s=" % opt.lstrip('--'))
 246                 opts, pargs = getopt.getopt(sys.argv[1:], "d:np:s:t:",
 247                     long_opts)
 248                 for opt, arg in opts:
 249                         if opt == "-n":
 250                                 sys.exit(0)
 251                         elif opt == "-d":
 252                                 repo_path = arg
 253                         elif opt == "-p":
 254                                 port = int(arg)
 255                                 port_provided = True
 256                         elif opt == "-s":
 257                                 threads = int(arg)
 258                                 if threads < THREADS_MIN:
 259                                         raise OptionError, \
 260                                             "minimum value is %d" % THREADS_MIN
 261                                 if threads > THREADS_MAX:
 262                                         raise OptionError, \
 263                                             "maximum value is %d" % THREADS_MAX


 279                                 # "," or any whitespace character as separators.
 280                                 if "," in arg:
 281                                         features = arg.split(",")
 282                                 else:
 283                                         features = arg.split()
 284 
 285                                 for f in features:
 286                                         if f not in debug_features:
 287                                                 raise OptionError, \
 288                                                     "Invalid debug feature: " \
 289                                                     "%s." % f
 290                                         debug_features[f] = True
 291                         elif opt in log_opts:
 292                                 if arg is None or arg == "":
 293                                         raise OptionError, \
 294                                             "You must specify a log " \
 295                                             "destination."
 296                                 log_routes[opt.lstrip("--log-")] = arg
 297                         elif opt == "--mirror":
 298                                 mirror = True













 299                         elif opt == "--proxy-base":
 300                                 # Attempt to decompose the url provided into
 301                                 # its base parts.  This is done so we can
 302                                 # remove any scheme information since we
 303                                 # don't need it.
 304                                 scheme, netloc, path, params, query, \
 305                                     fragment = urlparse.urlparse(arg,
 306                                     "http", allow_fragments=0)
 307 
 308                                 if not netloc:
 309                                         raise OptionError, "Unable to " \
 310                                             "determine the hostname from " \
 311                                             "the provided URL; please use a " \
 312                                             "fully qualified URL."
 313 
 314                                 scheme = scheme.lower()
 315                                 if scheme not in ("http", "https"):
 316                                         raise OptionError, "Invalid URL; http " \
 317                                             "and https are the only supported " \
 318                                             "schemes."


 426                     "must both be provided when using either option.")
 427         elif ssl_cert_file and ssl_key_file and not port_provided:
 428                 # If they didn't already specify a particular port, use the
 429                 # default SSL port instead.
 430                 port = SSL_PORT_DEFAULT
 431 
 432         # If the program is going to reindex, the port is irrelevant since
 433         # the program will not bind to a port.
 434         if not reindex:
 435                 available, msg = port_available(None, port)
 436                 if not available:
 437                         print "pkg.depotd: unable to bind to the specified " \
 438                             "port: %d. Reason: %s" % (port, msg)
 439                         sys.exit(1)
 440         else:
 441                 # Not applicable for reindexing operations.
 442                 content_root = None
 443 
 444         fork_allowed = not reindex
 445                 






 446         scfg = config.SvrConfig(repo_path, content_root, AUTH_DEFAULT,
 447             auto_create=not readonly, fork_allowed=fork_allowed,
 448             writable_root=writable_root)
 449 
 450         if readonly:
 451                 scfg.set_read_only()
 452 
 453         if mirror:
 454                 scfg.set_mirror()
 455 

 456         try:
 457                 scfg.init_dirs()
 458         except (errors.SvrConfigError, EnvironmentError), _e:
 459                 print "pkg.depotd: an error occurred while trying to " \
 460                     "initialize the depot repository directory " \
 461                     "structures:\n%s" % _e
 462                 sys.exit(1)
 463 
 464         key_data = None
 465         if not reindex and ssl_cert_file and ssl_key_file and \
 466             ssl_dialog != "builtin":
 467                 cmdline = None
 468                 def get_ssl_passphrase(*ignored):
 469                         p = None
 470                         try:
 471                                 p = subprocess.Popen(cmdline, shell=True,
 472                                         stdout=subprocess.PIPE,
 473                                         stderr=None)
 474                                 p.wait()
 475                         except Exception, __e:


 611                 #    http://cherrypy.org/wiki/BuiltinTools#tools.proxy
 612                 proxy_conf = {
 613                         "tools.proxy.on": True,
 614                         "tools.proxy.local": "",
 615                         "tools.proxy.base": proxy_base
 616                 }
 617 
 618                 # Now merge or add our proxy configuration information into the
 619                 # existing configuration.
 620                 for entry in proxy_conf:
 621                         conf["/"][entry] = proxy_conf[entry]
 622 
 623         scfg.acquire_in_flight()
 624         try:
 625                 scfg.acquire_catalog(rebuild=rebuild, verbose=True)
 626         except (catalog.CatalogPermissionsException, errors.SvrConfigError), _e:
 627                 emsg("pkg.depotd: %s" % _e)
 628                 sys.exit(1)
 629 
 630         try:




 631                 root = cherrypy.Application(depot.DepotHTTP(scfg,
 632                     repo_config_file))
 633         except rc.InvalidAttributeValueError, _e:
 634                 emsg("pkg.depotd: repository.conf error: %s" % _e)
 635                 sys.exit(1)
 636 
 637         try:
 638                 cherrypy.quickstart(root, config=conf)
 639         except Exception, _e:
 640                 emsg("pkg.depotd: unknown error starting depot server, " \
 641                     "illegal option value specified?")
 642                 emsg(_e)
 643                 sys.exit(1)


 111 
 112 class LogSink(object):
 113         """This is a dummy object that we can use to discard log entries
 114         without relying on non-portable interfaces such as /dev/null."""
 115 
 116         def write(self, *args, **kwargs):
 117                 """Discard the bits."""
 118                 pass
 119 
 120         def flush(self, *args, **kwargs):
 121                 """Discard the bits."""
 122                 pass
 123 
 124 def usage(text):
 125         if text:
 126                 emsg(text)
 127 
 128         print """\
 129 Usage: /usr/lib/pkg.depotd [-d repo_dir] [-p port] [-s threads]
 130            [-t socket_timeout] [--cfg-file] [--content-root] [--debug]
 131            [--log-access dest] [--log-errors dest] [--mirror] [--nasty]
 132            [--proxy-base url] [--readonly] [--rebuild] [--ssl-cert-file]
 133            [--ssl-dialog] [--ssl-key-file] [--writable-root dir]
 134 
 135         --cfg-file      The pathname of the file from which to read and to
 136                         write configuration information.
 137         --content-root  The file system path to the directory containing the
 138                         the static and other web content used by the depot's
 139                         browser user interface.  The default value is
 140                         '/usr/share/lib/pkg'.
 141         --debug         The name of a debug feature to enable; or a whitespace
 142                         or comma separated list of features to enable.  Possible
 143                         values are: headers.
 144         --log-access    The destination for any access related information
 145                         logged by the depot process.  Possible values are:
 146                         stderr, stdout, none, or an absolute pathname.  The
 147                         default value is stdout if stdout is a tty; otherwise
 148                         the default value is none.
 149         --log-errors    The destination for any errors or other information
 150                         logged by the depot process.  Possible values are:
 151                         stderr, stdout, none, or an absolute pathname.  The
 152                         default value is stderr.
 153         --mirror        Package mirror mode; publishing and metadata operations
 154                         disallowed.  Cannot be used with --readonly or
 155                         --rebuild.
 156         --nasty         Instruct the server to misbehave.  At random intervals
 157                         it will time-out, send bad responses, hang up on
 158                         clients, and generally be hostile.  The option
 159                         takes a value (1 to 100) for how nasty the server
 160                         should be.
 161         --proxy-base    The url to use as the base for generating internal
 162                         redirects and content.
 163         --readonly      Read-only operation; modifying operations disallowed.
 164                         Cannot be used with --mirror or --rebuild.
 165         --rebuild       Re-build the catalog from pkgs in depot.  Cannot be
 166                         used with --mirror or --readonly.
 167         --ssl-cert-file The absolute pathname to a PEM-encoded Certificate file.
 168                         This option must be used with --ssl-key-file.  Usage of
 169                         this option will cause the depot to only respond to SSL
 170                         requests on the provided port.
 171         --ssl-dialog    Specifies what method should be used to obtain the
 172                         passphrase needed to decrypt the file specified by
 173                         --ssl-key-file.  Supported values are: builtin,
 174                         exec:/path/to/program, or smf:fmri.  The default value
 175                         is builtin.
 176         --ssl-key-file  The absolute pathname to a PEM-encoded Private Key file.
 177                         This option must be used with --ssl-cert-file.  Usage of
 178                         this option will cause the depot to only respond to SSL
 179                         requests on the provided port.
 180         --writable-root The path to a directory to which the program has write


 190         def __init__(self, *args):
 191                 Exception.__init__(self, *args)
 192 
 193 if __name__ == "__main__":
 194 
 195         setlocale(locale.LC_ALL, "")
 196         gettext.install("pkg", "/usr/share/locale")
 197 
 198         debug_features = {
 199             "headers": False,
 200         }
 201         port = PORT_DEFAULT
 202         port_provided = False
 203         threads = THREADS_DEFAULT
 204         socket_timeout = SOCKET_TIMEOUT_DEFAULT
 205         readonly = READONLY_DEFAULT
 206         rebuild = REBUILD_DEFAULT
 207         reindex = REINDEX_DEFAULT
 208         proxy_base = None
 209         mirror = MIRROR_DEFAULT
 210         nasty = False
 211         nasty_value = 0
 212         repo_config_file = None
 213         ssl_cert_file = None
 214         ssl_key_file = None
 215         ssl_dialog = "builtin"
 216         writable_root = None
 217 
 218         if "PKG_REPO" in os.environ:
 219                 repo_path = os.environ["PKG_REPO"]
 220         else:
 221                 repo_path = REPO_PATH_DEFAULT
 222 
 223         try:
 224                 content_root = os.environ["PKG_DEPOT_CONTENT"]
 225         except KeyError:
 226                 try:
 227                         content_root = os.path.join(os.environ['PKG_HOME'],
 228                             'share/lib/pkg')
 229                 except KeyError:
 230                         content_root = CONTENT_PATH_DEFAULT
 231 
 232         # By default, if the destination for a particular log type is not
 233         # specified, this is where we will send the output.
 234         log_routes = {
 235             "access": "none",
 236             "errors": "stderr"
 237         }
 238         log_opts = ["--log-%s" % log_type for log_type in log_routes]
 239 
 240         # If stdout is a tty, then send access output there by default instead
 241         # of discarding it.
 242         if os.isatty(sys.stdout.fileno()):
 243                 log_routes["access"] = "stdout"
 244 
 245         opt = None
 246         try:
 247                 long_opts = ["cfg-file=", "content-root=", "debug=", "mirror",
 248                     "nasty=", "proxy-base=", "readonly", "rebuild",
 249                     "refresh-index", "ssl-cert-file=", "ssl-dialog=",
 250                     "ssl-key-file=", "writable-root="]
 251                 for opt in log_opts:
 252                         long_opts.append("%s=" % opt.lstrip('--'))
 253                 opts, pargs = getopt.getopt(sys.argv[1:], "d:np:s:t:",
 254                     long_opts)
 255                 for opt, arg in opts:
 256                         if opt == "-n":
 257                                 sys.exit(0)
 258                         elif opt == "-d":
 259                                 repo_path = arg
 260                         elif opt == "-p":
 261                                 port = int(arg)
 262                                 port_provided = True
 263                         elif opt == "-s":
 264                                 threads = int(arg)
 265                                 if threads < THREADS_MIN:
 266                                         raise OptionError, \
 267                                             "minimum value is %d" % THREADS_MIN
 268                                 if threads > THREADS_MAX:
 269                                         raise OptionError, \
 270                                             "maximum value is %d" % THREADS_MAX


 286                                 # "," or any whitespace character as separators.
 287                                 if "," in arg:
 288                                         features = arg.split(",")
 289                                 else:
 290                                         features = arg.split()
 291 
 292                                 for f in features:
 293                                         if f not in debug_features:
 294                                                 raise OptionError, \
 295                                                     "Invalid debug feature: " \
 296                                                     "%s." % f
 297                                         debug_features[f] = True
 298                         elif opt in log_opts:
 299                                 if arg is None or arg == "":
 300                                         raise OptionError, \
 301                                             "You must specify a log " \
 302                                             "destination."
 303                                 log_routes[opt.lstrip("--log-")] = arg
 304                         elif opt == "--mirror":
 305                                 mirror = True
 306                         elif opt == "--nasty":
 307                                 value_err = None
 308                                 try:
 309                                         nasty_value = int(arg)
 310                                 except ValueError, e:
 311                                         value_err = e
 312 
 313                                 if value_err or (nasty_value > 100 or
 314                                     nasty_value < 1):
 315                                         raise OptionError, "Invalid value " \
 316                                             "for nasty option.\n Please " \
 317                                             "choose a value between 1 and 100."
 318                                 nasty = True
 319                         elif opt == "--proxy-base":
 320                                 # Attempt to decompose the url provided into
 321                                 # its base parts.  This is done so we can
 322                                 # remove any scheme information since we
 323                                 # don't need it.
 324                                 scheme, netloc, path, params, query, \
 325                                     fragment = urlparse.urlparse(arg,
 326                                     "http", allow_fragments=0)
 327 
 328                                 if not netloc:
 329                                         raise OptionError, "Unable to " \
 330                                             "determine the hostname from " \
 331                                             "the provided URL; please use a " \
 332                                             "fully qualified URL."
 333 
 334                                 scheme = scheme.lower()
 335                                 if scheme not in ("http", "https"):
 336                                         raise OptionError, "Invalid URL; http " \
 337                                             "and https are the only supported " \
 338                                             "schemes."


 446                     "must both be provided when using either option.")
 447         elif ssl_cert_file and ssl_key_file and not port_provided:
 448                 # If they didn't already specify a particular port, use the
 449                 # default SSL port instead.
 450                 port = SSL_PORT_DEFAULT
 451 
 452         # If the program is going to reindex, the port is irrelevant since
 453         # the program will not bind to a port.
 454         if not reindex:
 455                 available, msg = port_available(None, port)
 456                 if not available:
 457                         print "pkg.depotd: unable to bind to the specified " \
 458                             "port: %d. Reason: %s" % (port, msg)
 459                         sys.exit(1)
 460         else:
 461                 # Not applicable for reindexing operations.
 462                 content_root = None
 463 
 464         fork_allowed = not reindex
 465                 
 466         if nasty:
 467                 scfg = config.NastySvrConfig(repo_path, content_root,
 468                     AUTH_DEFAULT, auto_create=not readonly,
 469                     fork_allowed=fork_allowed, writable_root=writable_root)
 470                 scfg.set_nasty(nasty_value)
 471         else:
 472                 scfg = config.SvrConfig(repo_path, content_root, AUTH_DEFAULT,
 473                     auto_create=not readonly, fork_allowed=fork_allowed,
 474                     writable_root=writable_root)
 475 
 476         if readonly:
 477                 scfg.set_read_only()
 478 
 479         if mirror:
 480                 scfg.set_mirror()
 481 
 482 
 483         try:
 484                 scfg.init_dirs()
 485         except (errors.SvrConfigError, EnvironmentError), _e:
 486                 print "pkg.depotd: an error occurred while trying to " \
 487                     "initialize the depot repository directory " \
 488                     "structures:\n%s" % _e
 489                 sys.exit(1)
 490 
 491         key_data = None
 492         if not reindex and ssl_cert_file and ssl_key_file and \
 493             ssl_dialog != "builtin":
 494                 cmdline = None
 495                 def get_ssl_passphrase(*ignored):
 496                         p = None
 497                         try:
 498                                 p = subprocess.Popen(cmdline, shell=True,
 499                                         stdout=subprocess.PIPE,
 500                                         stderr=None)
 501                                 p.wait()
 502                         except Exception, __e:


 638                 #    http://cherrypy.org/wiki/BuiltinTools#tools.proxy
 639                 proxy_conf = {
 640                         "tools.proxy.on": True,
 641                         "tools.proxy.local": "",
 642                         "tools.proxy.base": proxy_base
 643                 }
 644 
 645                 # Now merge or add our proxy configuration information into the
 646                 # existing configuration.
 647                 for entry in proxy_conf:
 648                         conf["/"][entry] = proxy_conf[entry]
 649 
 650         scfg.acquire_in_flight()
 651         try:
 652                 scfg.acquire_catalog(rebuild=rebuild, verbose=True)
 653         except (catalog.CatalogPermissionsException, errors.SvrConfigError), _e:
 654                 emsg("pkg.depotd: %s" % _e)
 655                 sys.exit(1)
 656 
 657         try:
 658                 if nasty:
 659                         root = cherrypy.Application(depot.NastyDepotHTTP(scfg,
 660                             repo_config_file))
 661                 else:
 662                         root = cherrypy.Application(depot.DepotHTTP(scfg,
 663                             repo_config_file))
 664         except rc.InvalidAttributeValueError, _e:
 665                 emsg("pkg.depotd: repository.conf error: %s" % _e)
 666                 sys.exit(1)
 667 
 668         try:
 669                 cherrypy.quickstart(root, config=conf)
 670         except Exception, _e:
 671                 emsg("pkg.depotd: unknown error starting depot server, " \
 672                     "illegal option value specified?")
 673                 emsg(_e)
 674                 sys.exit(1)