blob: 11b867bc902e166d9b6b276974fd8f5a18838e6f [file] [log] [blame] [edit]
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A progress class for tracking CrOS auto-update process.
This class is mainly designed for:
1. Set the pattern for generating the filenames of
track_status_file/execute_log_file.
track_status_file: Used for record the current step of CrOS auto-update
process. Only has one line.
execute_log_file: Used for record the whole logging info of the CrOS
auto-update process, including any debug information.
2. Write current auto-update process into the track_status_file.
3. Read current auto-update process from the track_status_file.
This file also offers external functions that are related to add/check/delete
the progress of the CrOS auto-update process.
"""
import datetime
import glob
import os
import re
from chromite.lib.xbuddy import cherrypy_log_util
# Module-local log function.
def _Log(message, *args):
return cherrypy_log_util.LogWithTag('CROS_UPDATE_PROGRESS', message, *args)
# Path for status tracking log.
_TRACK_LOG_FILE_PATH = '/tmp/auto-update/tracking_log/%s_%s.log'
# Pattern for status tracking log filename.
_TRACK_LOG_FILE_NAME_PATTERN = r'([^_]+)_([^_]+).log'
# The gap hour used in checking AU processes' count.
AU_PROCESS_HOUR_GAP = 3
def IsProcessAlive(pid):
"""Detect whether a process is alive.
Args:
pid: The process id.
Returns:
True if the process is still alive. False otherwise.
"""
path = '/proc/%s/stat' % pid
if not os.path.exists(path):
return False
with open(path, 'r') as fp:
stat = fp.readline().rstrip('\n')
return stat.split()[2] != 'Z'
def GetAllTrackStatusFileByTime():
"""Return all track status files existing in TRACK_LOG_FILE_PATH.
Returns:
A track status file list ordered by created time reversely.
"""
return sorted(glob.glob(_TRACK_LOG_FILE_PATH % ('*', '*')),
key=os.path.getctime, reverse=True)
def ParsePidFromTrackLogFileName(track_log_filename):
"""Parse pid from a given track log file's name.
The track log file's name for auto-update is fixed:
hostname_pid.log
This func is used to parse pid from a given track log file.
Args:
track_log_filename: the filename of the track log to be parsed.
Returns:
the parsed pid (int).
"""
match = re.match(_TRACK_LOG_FILE_NAME_PATTERN, track_log_filename)
try:
return int(match.groups()[1])
except (AttributeError, IndexError, ValueError) as e:
_Log('Cannot parse pid from track log file %s: %s', track_log_filename, e)
return None
def GetAllRunningAUProcess():
"""Get all the ongoing AU processes' pids from tracking logs.
This func only checks the tracking logs generated in latest several hours,
which is for avoiding the case that 'there's a running process whose id is
as the same as a previous AU process'.
Returns:
A list of background AU processes' pids.
"""
pids = []
now = datetime.datetime.now()
track_log_list = GetAllTrackStatusFileByTime()
# Only check log file created in 3 hours.
for track_log in track_log_list:
try:
created_time = datetime.datetime.fromtimestamp(
os.path.getctime(track_log))
if now - created_time >= datetime.timedelta(hours=AU_PROCESS_HOUR_GAP):
break
pid = ParsePidFromTrackLogFileName(os.path.basename(track_log))
if pid and IsProcessAlive(pid):
pids.append(pid)
except (ValueError, OSError) as e:
_Log('Error happened in getting pid from %s: %s', track_log, e)
return pids