def backup_database(version): logger.info("Backing up database before upgrade") if not helpers.backupVersionedFile(db.db_full_path(), version): logger.log_error_and_exit( "Database backup failed, abort upgrading database") else: logger.info("Proceeding with upgrade")
def execute(self): if not self.has_table("tv_shows") and not self.has_table("db_version"): queries = [ "CREATE TABLE db_version(db_version INTEGER, db_minor_version INTEGER);", "CREATE TABLE history(action NUMERIC, date NUMERIC, showid NUMERIC, season NUMERIC, episode NUMERIC, quality NUMERIC, resource TEXT, provider TEXT, version NUMERIC DEFAULT -1);", "CREATE TABLE imdb_info(indexer_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC);", "CREATE TABLE info(last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC);", "CREATE TABLE scene_numbering(indexer TEXT, indexer_id INTEGER, season INTEGER, episode INTEGER, scene_season INTEGER, scene_episode INTEGER, absolute_number NUMERIC, scene_absolute_number NUMERIC, PRIMARY KEY(indexer_id, season, episode));", "CREATE TABLE tv_shows(show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer NUMERIC, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC, archive_firstmatch NUMERIC, rls_require_words TEXT, rls_ignore_words TEXT, sports NUMERIC, anime NUMERIC, scene NUMERIC, default_ep_status NUMERIC DEFAULT -1, sub_use_sr_metadata NUMERIC DEFAULT 0);", "CREATE TABLE tv_episodes(episode_id INTEGER PRIMARY KEY, showid NUMERIC, indexerid NUMERIC, indexer TEXT, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT, file_size NUMERIC, release_name TEXT, subtitles TEXT, subtitles_searchcount NUMERIC, subtitles_lastsearch TIMESTAMP, is_proper NUMERIC, scene_season NUMERIC, scene_episode NUMERIC, absolute_number NUMERIC, scene_absolute_number NUMERIC, version NUMERIC DEFAULT -1, release_group TEXT);", "CREATE TABLE blacklist (show_id INTEGER, range TEXT, keyword TEXT);", "CREATE TABLE whitelist (show_id INTEGER, range TEXT, keyword TEXT);", "CREATE TABLE xem_refresh (indexer TEXT, indexer_id INTEGER PRIMARY KEY, last_refreshed INTEGER);", "CREATE TABLE indexer_mapping (indexer_id INTEGER, indexer NUMERIC, mindexer_id INTEGER, mindexer NUMERIC, PRIMARY KEY (indexer_id, indexer));", "CREATE UNIQUE INDEX idx_indexer_id ON tv_shows(indexer_id);", "CREATE INDEX idx_showid ON tv_episodes(showid);", "CREATE INDEX idx_sta_epi_air ON tv_episodes(status, episode, airdate);", "CREATE INDEX idx_sta_epi_sta_air ON tv_episodes(season, episode, status, airdate);", "CREATE INDEX idx_status ON tv_episodes(status,season,episode,airdate);", "CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid, airdate);", "INSERT INTO db_version(db_version, db_minor_version) VALUES (44, 3);" ] for query in queries: self.connection.action(query) else: cur_db_version = self.get_db_version() if cur_db_version < MIN_DB_VERSION: logger.log_error_and_exit(_( "Your database version ({cur_db_version}) is too old to migrate from what this version of SickChill supports ({MIN_DB_VERSION}).\nUpgrade using a previous version (tag) build 496 to build 501 of SickChill first or remove database file to begin fresh.".format(cur_db_version=cur_db_version, MIN_DB_VERSION=MIN_DB_VERSION))) if cur_db_version > MAX_DB_VERSION: logger.log_error_and_exit(_( "Your database version ({cur_db_version}) has been incremented past what this version of SickChill supports ({MAX_DB_VERSION}).\nIf you have used other forks of SickChill, your database may be unusable due to their modifications.".format(cur_db_version=cur_db_version, MAX_DB_VERSION=MAX_DB_VERSION)))
def restore_database(version): """ Restores a database to a previous version (backup file of version must still exist) :param version: Version to restore to :return: True if restore succeeds, False if it fails """ logger.info("Restoring database before trying upgrade again") if not sickchill.oldbeard.helpers.restoreVersionedFile(db_full_path(suffix="v" + str(version)), version): logger.log_error_and_exit("Database restore failed, abort upgrading database") return False else: return True
def migrate_config(self): """ Calls each successive migration until the config is the same version as SB expects """ if self.config_version > self.expected_config_version: logger.log_error_and_exit( """Your config version ({0:d}) has been incremented past what this version of SickChill supports ({1:d}). If you have used other forks or a newer version of SickChill, your config file may be unusable due to their modifications.""" .format(self.config_version, self.expected_config_version)) settings.CONFIG_VERSION = self.config_version while self.config_version < self.expected_config_version: next_version = self.config_version + 1 if next_version in self.migration_names: migration_name = ": " + self.migration_names[next_version] else: migration_name = "" logger.info("Backing up config before upgrade") if not helpers.backupVersionedFile(settings.CONFIG_FILE, self.config_version): logger.log_error_and_exit( "Config backup failed, abort upgrading config") else: logger.info("Proceeding with upgrade") # do the migration, expect a method named _migrate_v<num> logger.info("Migrating config up to version " + str(next_version) + migration_name) getattr(self, "_migrate_v" + str(next_version))() self.config_version = next_version # save new config after migration settings.CONFIG_VERSION = self.config_version logger.info("Saving config file to disk") sickchill.start.save_config()
def daemonize(self): """ Fork off as a daemon """ # An object is accessed for a non-existent member. # Access to a protected member of a client class # Make a non-session-leader child process try: pid = os.fork() # @UndefinedVariable - only available in UNIX if pid != 0: os._exit(0) except OSError as error: sys.stderr.write('fork #1 failed: {error_num}: {error_message}\n'.format (error_num=error.errno, error_message=error.strerror)) sys.exit(1) os.setsid() # @UndefinedVariable - only available in UNIX # https://github.com/SickChill/SickChill/issues/2969 # http://www.microhowto.info/howto/cause_a_process_to_become_a_daemon_in_c.html#idp23920 # https://www.safaribooksonline.com/library/view/python-cookbook/0596001673/ch06s08.html # Previous code simply set the umask to whatever it was because it was ANDing instead of OR-ing # Daemons traditionally run with umask 0 anyways and this should not have repercussions os.umask(0) # Make the child a session-leader by detaching from the terminal try: pid = os.fork() # @UndefinedVariable - only available in UNIX if pid != 0: os._exit(0) except OSError as error: sys.stderr.write('fork #2 failed: Error {error_num}: {error_message}\n'.format (error_num=error.errno, error_message=error.strerror)) sys.exit(1) # Write pid if self.create_pid: pid = os.getpid() logger.info('Writing PID: {pid} to {filename}'.format(pid=pid, filename=self.pid_file)) try: with os.fdopen(os.open(self.pid_file, os.O_CREAT | os.O_WRONLY, 0o644), 'w') as f_pid: f_pid.write('{0}\n'.format(pid)) except EnvironmentError as error: logger.log_error_and_exit('Unable to write PID file: {filename} Error {error_num}: {error_message}'.format (filename=self.pid_file, error_num=error.errno, error_message=error.strerror)) # Redirect all output sys.stdout.flush() sys.stderr.flush() devnull = getattr(os, 'devnull', '/dev/null') stdin = open(devnull) stdout = open(devnull, 'a+') stderr = open(devnull, 'a+') os.dup2(stdin.fileno(), getattr(sys.stdin, 'device', sys.stdin).fileno()) os.dup2(stdout.fileno(), getattr(sys.stdout, 'device', sys.stdout).fileno()) os.dup2(stderr.fileno(), getattr(sys.stderr, 'device', sys.stderr).fileno())