Beispiel #1
0
def pause_tracking_session(session):
    '''
    Close session and make a note of the session if it is open so that it can
    be resumed.
    '''

    if session.is_open():
        try:
            sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'a')
        except IOError as err:
            logger.error('Error opening the paused sessions file: {}'.format(err))
        else:
            with sessions_f:
                sessions_f.write(
                    '{}\n'.format(session.dumps())
                )

    session_end(session.path)

    closed_session = TrackingSession(name=session.name, pid=999999)
    shutil.move(
        session.path,
        '-{}'.format(time.time()).join(
            os.path.splitext(closed_session.path)
        )
    )
Beispiel #2
0
def get_tracker_events(old_only=False):
    """ Read the events log and return a dictionary with all of them.

        :param old_only: Don't return events from the current boot.
        :type old_only: boolean

        :returns: A dictionary suitable to be sent to the tracker endpoint.
        :rtype: dict
    """

    data = {'events': []}

    try:
        rf = open_locked(tracker_events_file, 'r')
    except IOError as e:
        logger.error("Error opening the tracker events file {}".format(e))
    else:
        with rf:
            for event_line in rf.readlines():
                try:
                    event = json.loads(event_line)
                except:
                    logger.warn("Found a corrupted event, skipping.")

                if _validate_event(event) and event['token'] != TOKEN:
                    data['events'].append(event)

    return data
Beispiel #3
0
def _open_uuids():
    """Helper function to open the uuids file and load the JSON inside.

    Returns:
        file, dict: The opened uuids file and its JSON data.
    """

    newly_created = False
    uuids_file = None
    data = dict()

    if not os.path.exists(TRACKER_UUIDS_PATH):
        touch(TRACKER_UUIDS_PATH)
        newly_created = True

    try:
        uuids_file = open_locked(TRACKER_UUIDS_PATH, 'r+')
    except (IOError, OSError) as error:
        logger.error('Error while opening uuids file: {}'.format(error))

    else:
        if newly_created:
            uuids_file.write(json.dumps(data))
            uuids_file.seek(0)

        try:
            data = json.load(uuids_file)
        except ValueError as error:
            logger.error('File uuids does not contain valid JSON: {}'.format(error))

    return uuids_file, data
Beispiel #4
0
def get_tracker_events(old_only=False):
    """ Read the events log and return a dictionary with all of them.

        :param old_only: Don't return events from the current boot.
        :type old_only: boolean

        :returns: A dictionary suitable to be sent to the tracker endpoint.
        :rtype: dict
    """

    data = {'events': []}

    try:
        rf = open_locked(tracker_events_file, 'r')
    except IOError as e:
        logger.error("Error opening the tracker events file {}".format(e))
    else:
        with rf:
            for event_line in rf.readlines():
                try:
                    event = json.loads(event_line)
                except:
                    logger.warn("Found a corrupted event, skipping.")

                if _validate_event(event) and event['token'] != TOKEN:
                    data['events'].append(event)

    return data
def generate_tracker_token():
    """
        Generating the token is a simple md5hash of the current time.

        The token is saved to the `tracker_token_file`.

        :returns: The token.
        :rtype: str
    """

    token = hashlib.md5(str(time.time())).hexdigest()

    ensure_dir(tracker_dir)
    try:
        f = open_locked(tracker_token_file, 'w')
    except IOError as e:
        logger.error(
            "Error opening tracker token file (generate) {}".format(e))
    else:
        with f:
            f.write(token)
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_token_file)

    # Make sure that the events file exist
    try:
        f = open(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        f.close()
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)

    return token
Beispiel #6
0
def clear_tracker_events(old_only=True):
    """ Truncate the events file, removing all the cached data.

        :param old_only: Don't remove data from the current boot.
        :type old_only: boolean
    """
    try:
        rf = open_locked(tracker_events_file, 'r')
    except IOError as e:
        logger.error("Error opening tracking events file {}".format(e))
    else:
        with rf:
            events = []
            for event_line in rf.readlines():
                try:
                    event = json.loads(event_line)
                    if 'token' in event and event['token'] == TOKEN:
                        events.append(event_line)
                except:
                    logger.warn("Found a corrupted event, skipping.")

            with open(tracker_events_file, 'w') as wf:
                for event_line in events:
                    wf.write(event_line)
            if 'SUDO_USER' in os.environ:
                chown_path(tracker_events_file)
Beispiel #7
0
def get_paused_sessions():
    if not os.path.exists(PAUSED_SESSIONS_FILE):
        return []

    try:
        sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'r')
    except IOError as err:
        logger.error('Error opening the paused sessions file: {}'.format(err))
        return []
    else:
        with sessions_f:
            paused_sessions = []
            for session in sessions_f:
                if not session:
                    continue

                try:
                    new_session = TrackingSession.loads(session)
                except TypeError:
                    logger.warn(
                        'Failed to process session: {}'.format(session))
                    continue
                paused_sessions.append(new_session)

            return paused_sessions
Beispiel #8
0
def track_data(name, data):
    """ Track arbitrary data.

        Calling this function will generate a data tracking event.

        :param name: The identifier of the data.
        :type name: str

        :param data: Arbitrary data, must be compatible with JSON.
        :type data: dict, list, str, int, float, None
    """

    event = {
        'type': 'data',
        'time': int(time.time()),
        'timezone_offset': get_utc_offset(),
        'os_version': OS_VERSION,
        'cpu_id': CPU_ID,
        'token': TOKEN,
        'language': LANGUAGE,
        'name': str(name),
        'data': data
    }

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        with af:
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
Beispiel #9
0
def clear_tracker_events(old_only=True):
    """ Truncate the events file, removing all the cached data.

        :param old_only: Don't remove data from the current boot.
        :type old_only: boolean
    """
    try:
        rf = open_locked(tracker_events_file, 'r')
    except IOError as e:
        logger.error("Error opening tracking events file {}".format(e))
    else:
        with rf:
            events = []
            for event_line in rf.readlines():
                try:
                    event = json.loads(event_line)
                    if 'token' in event and event['token'] == TOKEN:
                        events.append(event_line)
                except:
                    logger.warn("Found a corrupted event, skipping.")

            with open(tracker_events_file, 'w') as wf:
                for event_line in events:
                    wf.write(event_line)
            if 'SUDO_USER' in os.environ:
                chown_path(tracker_events_file)
Beispiel #10
0
def session_log(name, started, length):
    """ Log a session that was tracked outside of the tracker.

        :param name: The identifier of the session.
        :type name: str

        :param started: When was the session started (UTC unix timestamp).
        :type started: int

        :param length: Length of the session in seconds.
        :param started: int
    """

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error while opening events file: {}".format(e))
    else:
        with af:
            session = {
                'name': name,
                'started': int(started),
                'elapsed': int(length)
            }

            event = get_session_event(session)
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
Beispiel #11
0
def session_end(session_file):
    if not os.path.exists(session_file):
        msg = "Someone removed the tracker file, the runtime of this " \
              "app will not be logged"
        logger.warn(msg)
        return

    try:
        rf = open_locked(session_file, 'r')
    except IOError as e:
        logger.error("Error opening the tracker session file {}".format(e))
    else:
        with rf:
            data = json.load(rf)

            data['elapsed'] = int(time.time()) - data['started']
            data['finished'] = True

            try:
                wf = open(session_file, 'w')
            except IOError as e:
                logger.error(
                    "Error opening the tracker session file {}".format(e))
            else:
                with wf:
                    json.dump(data, wf)
        if 'SUDO_USER' in os.environ:
            chown_path(session_file)
 def open(self, mode):
     try:
         session_f = open_locked(self.path, mode)
     except IOError as exc:
         logger.error(
             'Error opening the tracking session file "{f}": {err}'.format(
                 f=self.path, err=exc))
     else:
         yield session_f
Beispiel #13
0
def session_start(name, pid=None, ignore_pause=False):
    if not pid:
        pid = os.getpid()
    pid = int(pid)

    if not ignore_pause and is_tracking_paused():
        session = TrackingSession(name=name, pid=pid)
        try:
            paused_sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'a')
        except IOError as err:
            logger.error(
                'Error while opening the paused sessions file: {}'.format(err)
            )
        else:
            paused_sessions_f.write(
                '{}\n'.format(session.dumps())
            )

            return session.path

    data = {
        'pid': pid,
        'name': name,
        'started': int(time.time()),
        'elapsed': 0,
        'app_session_id': str(uuid5(uuid1(), name + str(pid))),
        'finished': False,
        'token-system': TOKEN
    }

    path = get_session_file_path(data['name'], data['pid'])

    try:
        f = open_locked(path, 'w')
    except IOError as e:
        logger.error("Error opening tracker session file {}".format(e))
    else:
        with f:
            json.dump(data, f)
        if 'SUDO_USER' in os.environ:
            chown_path(path)

    return path
 def open(self, mode):
     try:
         session_f = open_locked(self.path, mode)
     except IOError as exc:
         logger.error(
             'Error opening the tracking session file "{f}": {err}'
             .format(f=self.path, err=exc)
         )
     else:
         yield session_f
Beispiel #15
0
def get_session_unique_id(name, pid):
    data = {}
    tracker_session_file = get_session_file_path(name, pid)
    try:
        af = open_locked(tracker_session_file, 'r')
    except (IOError, OSError) as e:
        logger.error("Error while opening session file: {}".format(e))
    else:
        with af:
            try:
                data = json.load(af)
            except ValueError as e:
                logger.error("Session file is not a valid JSON")

    return data.get('app_session_id', "")
Beispiel #16
0
def track_action(name):
    """ Trigger an action tracking event.

        :param name: The identifier of the action.
        :type name: str
    """

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        with af:
            event = get_action_event(name)
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
Beispiel #17
0
def track_action(name):
    """ Trigger an action tracking event.

        :param name: The identifier of the action.
        :type name: str
    """

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        with af:
            event = get_action_event(name)
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
Beispiel #18
0
def pause_tracking_sessions():
    '''
    Loop through all the alive sessions, pausing each one.
    '''

    open_sessions = get_open_sessions()

    # Mark paused in the event of no sessions
    if len(open_sessions) == 0:
        try:
            paused_sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'a')
        except IOError as err:
            logger.error(
                'Error while opening the paused sessions file: {}'.format(err))
        else:
            with paused_sessions_f:
                pass

    for session in open_sessions:
        pause_tracking_session(session)
Beispiel #19
0
def unpause_tracking_session(session):
    '''
    Restart the session if the process is still alive and remove record of this
    paused session.
    '''

    if session.is_open():
        session_start(session.name, session.pid, ignore_pause=True)

    paused_sessions = get_paused_sessions()

    try:
        paused_sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'w')
    except IOError as e:
        logger.error("Error while opening events file: {}".format(e))
    else:
        with paused_sessions_f:
            for paused_session in paused_sessions:
                if paused_session != session:
                    paused_sessions_f.write('{}\n'.format(
                        paused_session.dumps()))
def pause_tracking_sessions():
    '''
    Loop through all the alive sessions, pausing each one.
    '''

    open_sessions = get_open_sessions()

    # Mark paused in the event of no sessions
    if len(open_sessions) == 0:
        try:
            paused_sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'a')
        except IOError as err:
            logger.error(
                'Error while opening the paused sessions file: {}'.format(err)
            )
        else:
            with paused_sessions_f:
                pass

    for session in open_sessions:
        pause_tracking_session(session)
Beispiel #21
0
def track_data(name, data):
    """ Track arbitrary data.

        Calling this function will generate a data tracking event.

        :param name: The identifier of the data.
        :type name: str

        :param data: Arbitrary data, must be compatible with JSON.
        :type data: dict, list, str, int, float, None
    """

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        with af:
            event = get_data_event(name, data)
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
Beispiel #22
0
def track_data(name, data):
    """ Track arbitrary data.

        Calling this function will generate a data tracking event.

        :param name: The identifier of the data.
        :type name: str

        :param data: Arbitrary data, must be compatible with JSON.
        :type data: dict, list, str, int, float, None
    """

    try:
        af = open_locked(tracker_events_file, 'a')
    except IOError as e:
        logger.error("Error opening tracker events file {}".format(e))
    else:
        with af:
            event = get_data_event(name, data)
            af.write(json.dumps(event) + "\n")
        if 'SUDO_USER' in os.environ:
            chown_path(tracker_events_file)
def unpause_tracking_session(session):
    '''
    Restart the session if the process is still alive and remove record of this
    paused session.
    '''

    if session.is_open():
        session_start(session.name, session.pid, ignore_pause=True)

    paused_sessions = get_paused_sessions()

    try:
        paused_sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'w')
    except IOError as e:
        logger.error("Error while opening events file: {}".format(e))
    else:
        with paused_sessions_f:
            for paused_session in paused_sessions:
                if paused_session != session:
                    paused_sessions_f.write(
                        '{}\n'.format(paused_session.dumps())
                    )
def load_token():
    """
        Reads the tracker token from the token file. If the file doesn't
        exists, it regenerates it.

        The token is regenerated on each boot and is inserted into every
        event to link together events that happened during the same start
        of the OS.

        :returns: The token.
        :rtype: str
    """

    if os.path.exists(tracker_token_file):
        try:
            f = open_locked(tracker_token_file, 'r')
        except IOError as e:
            logger.error("Error opening tracker token file {}".format(e))
        else:
            with f:
                return f.read().strip()
    else:
        return generate_tracker_token()
def get_paused_sessions():
    if not os.path.exists(PAUSED_SESSIONS_FILE):
        return []

    try:
        sessions_f = open_locked(PAUSED_SESSIONS_FILE, 'r')
    except IOError as err:
        logger.error('Error opening the paused sessions file: {}'.format(err))
        return []
    else:
        with sessions_f:
            paused_sessions = []
            for session in sessions_f:
                if not session:
                    continue

                try:
                    new_session = TrackingSession.loads(session)
                except TypeError:
                    logger.warn('Failed to process session: {}'.format(session))
                    continue
                paused_sessions.append(new_session)

            return paused_sessions
Beispiel #26
0
def add_runtime_to_app(app, runtime):
    """ Saves the tracking data for a given application.

        Appends a time period to a given app's runtime stats and raises
        starts by one. Apart from the total values, it also updates the
        weekly stats.

        This function uses advisory file locks (see flock(2)) to avoid
        races between different applications saving their tracking data
        at the same time.

        :param app: The name of the application.
        :type app: str
        :param runtime: For how long was the app running.
        :type runtime: number
    """

    if not app or app == 'kano-tracker':
        return

    if not is_number(runtime):
        return

    runtime = float(runtime)

    app = app.replace('.', '_')

    # Make sure no one else is accessing this file
    app_state_file = get_app_state_file('kano-tracker')

    try:
        tracker_store = open_locked(app_state_file, 'r')
    except IOError as e:
        logger.error("Error opening app state file {}".format(e))
    else:
        app_stats = load_app_state_variable('kano-tracker', 'app_stats')
        if not app_stats:
            app_stats = dict()

        try:
            app_stats[app]['starts'] += 1
            app_stats[app]['runtime'] += runtime
        except Exception:
            app_stats[app] = {
                'starts': 1,
                'runtime': runtime,
            }

        # Record usage data on per-week basis
        if 'weekly' not in app_stats[app]:
            app_stats[app]['weekly'] = {}

        week = str(get_nearest_previous_monday())
        if week not in app_stats[app]['weekly']:
            app_stats[app]['weekly'][week] = {
                'starts': 0,
                'runtime': 0
            }

        app_stats[app]['weekly'][week]['starts'] += 1
        app_stats[app]['weekly'][week]['runtime'] += runtime

        save_app_state_variable('kano-tracker', 'app_stats', app_stats)

        # Close the lock
        tracker_store.close()
Beispiel #27
0
def add_runtime_to_app(app, runtime):
    """ Saves the tracking data for a given application.

        Appends a time period to a given app's runtime stats and raises
        starts by one. Apart from the total values, it also updates the
        weekly stats.

        This function uses advisory file locks (see flock(2)) to avoid
        races between different applications saving their tracking data
        at the same time.

        :param app: The name of the application.
        :type app: str
        :param runtime: For how long was the app running.
        :type runtime: number
    """

    if not app or app == 'kano-tracker':
        return

    if not is_number(runtime):
        return

    runtime = float(runtime)

    app = app.replace('.', '_')

    # Make sure no one else is accessing this file
    app_state_file = get_app_state_file('kano-tracker')

    try:
        tracker_store = open_locked(app_state_file, 'r')
    except IOError as e:
        logger.error("Error opening app state file {}".format(e))
    else:
        app_stats = load_app_state_variable('kano-tracker', 'app_stats')
        if not app_stats:
            app_stats = dict()

        try:
            app_stats[app]['starts'] += 1
            app_stats[app]['runtime'] += runtime
        except Exception:
            app_stats[app] = {
                'starts': 1,
                'runtime': runtime,
            }

        # Record usage data on per-week basis
        if 'weekly' not in app_stats[app]:
            app_stats[app]['weekly'] = {}

        week = str(get_nearest_previous_monday())
        if week not in app_stats[app]['weekly']:
            app_stats[app]['weekly'][week] = {
                'starts': 0,
                'runtime': 0
            }

        app_stats[app]['weekly'][week]['starts'] += 1
        app_stats[app]['weekly'][week]['runtime'] += runtime

        save_app_state_variable('kano-tracker', 'app_stats', app_stats)

        # Close the lock
        tracker_store.close()