def main(db, service_id_file, logfile=False, verbose=False, quiet=False, rate=DEFAULT_SCANS,
		timeout_sec=DEFAULT_WAIT, sni=False):
	"""
	Run the main program.
	Scan a list of services and update Observation records in the notary database.
	"""

	global stats
	global results

	stats = GlobalStats()
	results = ResultStore()

	notary_logs.setup_logs(logfile, LOGFILE, verbose=verbose, quiet=quiet)

	start_time = time.time()
	localtime = time.asctime(time.localtime(start_time))

	# read all service names to start;
	# otherwise the database can lock up
	# if we're accepting data piped from another process
	all_sids = [line.rstrip() for line in service_id_file]

	print("Starting scan of %s service-ids at: %s" % (len(all_sids), localtime))
	print("INFO: *** Timeout = %s sec  Scans-per-second = %s" % \
	    (timeout_sec, rate))
	db.report_metric('ServiceScanStart', "ServiceCount: " + str(len(all_sids)))

	# create a thread to scan each service
	# and record results as they come in
	for sid in all_sids:
		try:
			# ignore non SSL services
			# TODO: use a regex instead
			if sid.split(",")[1] == notary_common.SSL_TYPE:
				stats.num_started += 1
				t = ScanThread(db, sid, stats, timeout_sec, sni)
				t.start()

			if (stats.num_started % rate) == 0:
				time.sleep(1)
				_record_observations_in_db(db, results.get())

				so_far = int(time.time() - start_time)
				logging.info("%s seconds passed.  %s complete, %s " \
					"failures.  %s Active threads" % \
					(so_far, stats.num_completed,
					stats.failures, stats.active_threads))
				logging.info("  details: timeouts = %s, " \
					"ssl-alerts = %s, no-route = %s, " \
					"conn-refused = %s, conn-reset = %s,"\
					"dns = %s, socket = %s, other = %s" % \
					(stats.failure_timeouts,
					stats.failure_ssl_alert,
					stats.failure_no_route,
					stats.failure_conn_refused,
					stats.failure_conn_reset,
					stats.failure_dns,
					stats.failure_socket,
					stats.failure_other))

			if stats.num_started  % 1000 == 0:
				if (verbose):
					logging.info("long running threads")
					cur_time = time.time()
					for sid in stats.threads.keys():
						spawn_time = stats.threads.get(sid, cur_time)
						duration = cur_time - spawn_time
						if duration > 20:
							logging.info("'%s' has been running for %s" %\
							 (sid, duration))

		except IndexError:
			logging.error("Service '%s' has no index [1] after splitting on ','.\n" % (sid))
		except KeyboardInterrupt:
			exit(1)

	# finishing the for-loop means we kicked-off all threads,
	# but they may not be done yet.  Wait for a bit, if needed.
	giveup_time = time.time() + (2 * timeout_sec)
	while stats.active_threads > 0:
		time.sleep(1)
		if time.time() > giveup_time:
			if stats.active_threads > 0:
				logging.error("Giving up scans after {0}. {1} threads still active!".format(
					giveup_time, stats.active_threads))
			break

	# record any observations made since we finished the
	# main for-loop
	_record_observations_in_db(db, results.get())

	duration = int(time.time() - start_time)
	localtime = time.asctime(time.localtime(start_time))
	print("Ending scan at: %s" % localtime)
	print("Scan of %s services took %s seconds.  %s Failures" % \
		(stats.num_started, duration, stats.failures))
	db.report_metric('ServiceScanStop')
	exit(0)