/*
* GNOME Logs - View and search logs
* Copyright (C) 2013 Red Hat, Inc.
* Copyright (C) 2015 Ekaterina Gerasimova
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "config.h"
#include "gl-util.h"
#include
#include
#include
/**
* GlUtilTimestamps:
* @GL_UTIL_TIMESTAMPS_SAME_DAY: the timestamps have the same year, month and
* day
* @GL_UTIL_TIMESTAMPS_SAME_YEAR: the timestamps have the same year, but
* different months and days
* @GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR: the timestamps have different years,
* months and days
*
* Date string comparison result, used for formatting a date into an
* appropriate string.
*/
typedef enum
{
GL_UTIL_TIMESTAMPS_SAME_DAY,
GL_UTIL_TIMESTAMPS_SAME_YEAR,
GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR
} GlUtilTimestamps;
static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
static const gchar CLOCK_FORMAT[] = "clock-format";
/**
* compare_timestamps:
* @a: a date
* @b: a date to compare with
*
* Compare @a to @b and return how similar the dates are.
*
* Returns: a value from GlUtilTimestamps
*/
static GlUtilTimestamps
compare_timestamps (GDateTime *a,
GDateTime *b)
{
gint ayear, amonth, aday;
gint byear, bmonth, bday;
g_date_time_get_ymd (a, &ayear, &amonth, &aday);
g_date_time_get_ymd (b, &byear, &bmonth, &bday);
if (ayear != byear)
{
return GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR;
}
/* Same year, month and day. */
if ((amonth == bmonth) && (aday == bday))
{
return GL_UTIL_TIMESTAMPS_SAME_DAY;
}
/* Same year, but differing by month or day. */
return GL_UTIL_TIMESTAMPS_SAME_YEAR;
}
/**
* gl_util_timestamp_to_display:
* @microsecs: number of microseconds since the Unix epoch in UTC
* @now: the time to compare with
* @format: clock format (12 or 24 hour)
*
* Return a human readable time, corresponding to @microsecs, using an
* appropriate @format after comparing it to @now and discarding unnecessary
* elements (for example, return only time if the date is today).
*
* Returns: a newly-allocated human readable string which represents @microsecs
*/
gchar *
gl_util_timestamp_to_display (guint64 microsecs,
GDateTime *now,
GlUtilClockFormat format,
gboolean show_second)
{
GDateTime *datetime;
GDateTime *local;
gchar *time = NULL;
datetime = g_date_time_new_from_unix_utc (microsecs / G_TIME_SPAN_SECOND);
if (datetime == NULL)
{
g_warning ("Error converting timestamp to time value");
goto out;
}
local = g_date_time_to_local (datetime);
switch (format)
{
case GL_UTIL_CLOCK_FORMAT_12HR:
switch (compare_timestamps (local, now))
{
case GL_UTIL_TIMESTAMPS_SAME_DAY:
if (show_second)
{
/* Translators: timestamp format for events on the
* current day, showing the time with seconds in
* 12-hour format. */
time = g_date_time_format (local, _("%l:%M:%S %p"));
}
else
{
/* Translators: timestamp format for events on the
* current day, showing the time without seconds in
* 12-hour format. */
time = g_date_time_format (local, _("%l:%M %p"));
}
break;
case GL_UTIL_TIMESTAMPS_SAME_YEAR:
if (show_second)
{
time = g_date_time_format (local,
/* Translators: timestamp format for events in
* the current year, showing the abbreviated
* month name, day of the month and the time
* with seconds in 12-hour format. */
_("%b %e %l:%M:%S %p"));
}
else
{
/* Translators: timestamp format for events in the
* current year, showing the abbreviated month name,
* day of the month and the time without seconds in
* 12-hour format. */
time = g_date_time_format (local, _("%b %e %l:%M %p"));
}
break;
case GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR:
if (show_second)
{
time = g_date_time_format (local,
/* Translators: timestamp format for events in
* a different year, showing the abbreviated
* month name, day of the month, year and the
* time with seconds in 12-hour format. */
_("%b %e %Y %l:%M:%S %p"));
}
else
{
time = g_date_time_format (local,
/* Translators: timestamp format for events in
* a different year, showing the abbreviated
* month name day of the month, year and the
* time without seconds in 12-hour format. */
_("%b %e %Y %l:%M %p"));
}
break;
default:
g_assert_not_reached ();
}
break;
case GL_UTIL_CLOCK_FORMAT_24HR:
switch (compare_timestamps (local, now))
{
case GL_UTIL_TIMESTAMPS_SAME_DAY:
if (show_second)
{
/* Translators: timestamp format for events on the
* current day, showing the time with seconds in
* 24-hour format. */
time = g_date_time_format (local, _("%H:%M:%S"));
}
else
{
/* Translators: timestamp format for events on the
* current day, showing the time without seconds in
* 24-hour format. */
time = g_date_time_format (local, _("%H:%M"));
}
break;
case GL_UTIL_TIMESTAMPS_SAME_YEAR:
if (show_second)
{
/* Translators: timestamp format for events in the
* current year, showing the abbreviated month name,
* day of the month and the time with seconds in
* 24-hour format. */
time = g_date_time_format (local, _("%b %e %H:%M:%S"));
}
else
{
/* Translators: timestamp format for events in the
* current year, showing the abbreviated month name,
* day of the month and the time without seconds in
* 24-hour format. */
time = g_date_time_format (local, _("%b %e %H:%M"));
}
break;
case GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR:
if (show_second)
{
time = g_date_time_format (local,
/* Translators: timestamp format for events in
* a different year, showing the abbreviated
* month name, day of the month, year and the
* time with seconds in 24-hour format. */
_("%b %e %Y %H:%M:%S"));
}
else
{
/* Translators: timestamp format for events in a
* different year, showing the abbreviated month name,
* day of the month, year and the time without seconds
* in 24-hour format. */
time = g_date_time_format (local, _("%b %e %Y %H:%M"));
}
break;
default:
g_assert_not_reached ();
}
break;
default:
g_assert_not_reached ();
}
g_date_time_unref (datetime);
g_date_time_unref (local);
if (time == NULL)
{
g_warning ("Error converting datetime to string");
}
out:
return time;
}
gint
gl_util_get_uid (void)
{
GCredentials *creds;
uid_t uid;
creds = g_credentials_new ();
uid = g_credentials_get_unix_user (creds, NULL);
g_object_unref (creds);
return uid;
}
gchar *
gl_util_boot_time_to_display (guint64 realtime_first,
guint64 realtime_last)
{
gchar *time_first;
gchar *time_last;
gchar *time_display;
GDateTime *now;
GSettings *settings;
GlUtilClockFormat clock_format;
/* TODO: Monitor and propagate any GSettings changes. */
settings = g_settings_new (DESKTOP_SCHEMA);
clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
g_object_unref (settings);
now = g_date_time_new_now_local ();
time_first = gl_util_timestamp_to_display (realtime_first,
now, clock_format, FALSE);
time_last = gl_util_timestamp_to_display (realtime_last,
now, clock_format, FALSE);
/* Transltors: the first string is the earliest timestamp of the boot,
* and the second string is the newest timestamp. An example string might
* be '08:10 - 08:30' */
time_display = g_strdup_printf (_("%s – %s"), time_first, time_last);
g_date_time_unref (now);
g_free (time_first);
g_free (time_last);
return time_display;
}
/**
* Determine journal storage type:
*
* Test existence of possible journal storage paths.
*
* Returns: a value from GlJournalStorage
*/
GlJournalStorage
gl_util_journal_storage_type (void)
{
g_autofree gchar *run_path = NULL;
g_autofree gchar *var_path = NULL;
gchar ids[33];
gint ret;
sd_id128_t machine_id;
ret = sd_id128_get_machine (&machine_id);
if (ret < 0)
{
g_critical ("Error getting machine id: %s", g_strerror (-ret));
}
sd_id128_to_string (machine_id, ids);
run_path = g_build_filename ("/run/log/journal/", ids, NULL);
var_path = g_build_filename ("/var/log/journal/", ids, NULL);
if (g_file_test (run_path, G_FILE_TEST_EXISTS))
{
return GL_JOURNAL_STORAGE_VOLATILE;
}
else if (g_file_test (var_path, G_FILE_TEST_EXISTS))
{
return GL_JOURNAL_STORAGE_PERSISTENT;
}
else
{
return GL_JOURNAL_STORAGE_NONE;
}
}
gboolean
gl_util_can_read_system_journal (GlJournalStorage storage_type)
{
GFile *file;
GFileInfo *info;
gint ret;
gchar *path;
gchar ids[33];
sd_id128_t machine;
ret = sd_id128_get_machine (&machine);
if (ret < 0)
{
g_critical ("Error getting machine id: %s", g_strerror (-ret));
}
sd_id128_to_string (machine, ids);
if (storage_type == GL_JOURNAL_STORAGE_PERSISTENT)
{
path = g_build_filename ("/var/log/journal", ids, "system.journal", NULL);
}
else if (storage_type == GL_JOURNAL_STORAGE_VOLATILE)
{
path = g_build_filename ("/run/log/journal", ids, "system.journal", NULL);
}
else
{
path = "/dev/null";
}
file = g_file_new_for_path (path);
info = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
g_free (path);
g_object_unref (file);
if (g_file_info_get_attribute_boolean (info,
G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
{
g_object_unref (info);
return TRUE;
}
else
{
g_object_unref (info);
return FALSE;
}
}
gboolean
gl_util_can_read_user_journal (void)
{
GFile *file;
GFileInfo *info;
gint ret;
gchar *path;
gchar ids[33];
gchar *filename;
gchar *uid;
uid_t user_id;
sd_id128_t machine;
GError *error = NULL;
GCredentials *credentials;
credentials = g_credentials_new ();
user_id = g_credentials_get_unix_user (credentials, &error);
if (error != NULL)
{
g_debug ("Unable to get uid: %s", error->message);
g_error_free (error);
}
uid = g_strdup_printf ("%d", user_id);
filename = g_strconcat ("/user-", uid, ".journal", NULL);
ret = sd_id128_get_machine (&machine);
if (ret < 0)
{
g_critical ("Error getting machine id: %s", g_strerror (-ret));
}
sd_id128_to_string (machine, ids);
path = g_build_filename ("/var/log/journal", ids, filename, NULL);
file = g_file_new_for_path (path);
info = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
g_free (uid);
g_free (path);
g_free (filename);
g_object_unref (file);
g_object_unref (credentials);
if (g_file_info_get_attribute_boolean (info,
G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
{
g_object_unref (info);
return TRUE;
}
else
{
g_object_unref (info);
return FALSE;
}
}
static void
add_css_variations (GString *s,
const char *variations)
{
const char *p;
const char *sep = "";
if (variations == NULL || variations[0] == '\0')
{
g_string_append (s, "normal");
return;
}
p = variations;
while (p && *p)
{
const char *start;
const char *end, *end2;
double value;
char name[5];
while (g_ascii_isspace (*p)) p++;
start = p;
end = strchr (p, ',');
if (end && (end - p < 6))
goto skip;
name[0] = p[0];
name[1] = p[1];
name[2] = p[2];
name[3] = p[3];
name[4] = '\0';
p += 4;
while (g_ascii_isspace (*p)) p++;
if (*p == '=') p++;
if (p - start < 5)
goto skip;
value = g_ascii_strtod (p, (char **) &end2);
while (end2 && g_ascii_isspace (*end2)) end2++;
if (end2 && (*end2 != ',' && *end2 != '\0'))
goto skip;
g_string_append_printf (s, "%s\"%s\" %g", sep, name, value);
sep = ", ";
skip:
p = end ? end + 1 : NULL;
}
}
/**
* This function is orignally written in gtk/gtkfontbutton.c of gtk+ project.
*/
gchar *
pango_font_description_to_css (PangoFontDescription *desc)
{
GString *s;
PangoFontMask set;
/* This line is modified with respect to gtk/gtkfontbutton.c */
s = g_string_new ("{ ");
set = pango_font_description_get_set_fields (desc);
if (set & PANGO_FONT_MASK_FAMILY)
{
g_string_append (s, "font-family: \"");
g_string_append (s, pango_font_description_get_family (desc));
g_string_append (s, "\"; ");
}
if (set & PANGO_FONT_MASK_STYLE)
{
switch (pango_font_description_get_style (desc))
{
case PANGO_STYLE_NORMAL:
g_string_append (s, "font-style: normal; ");
break;
case PANGO_STYLE_OBLIQUE:
g_string_append (s, "font-style: oblique; ");
break;
case PANGO_STYLE_ITALIC:
g_string_append (s, "font-style: italic; ");
break;
default:
break;
}
}
if (set & PANGO_FONT_MASK_VARIANT)
{
switch (pango_font_description_get_variant (desc))
{
case PANGO_VARIANT_NORMAL:
g_string_append (s, "font-variant: normal; ");
break;
case PANGO_VARIANT_SMALL_CAPS:
g_string_append (s, "font-variant: small-caps; ");
break;
case PANGO_VARIANT_ALL_SMALL_CAPS:
g_string_append (s, "font-variant: all-small-caps; ");
break;
case PANGO_VARIANT_PETITE_CAPS:
g_string_append (s, "font-variant: petite-caps; ");
break;
case PANGO_VARIANT_ALL_PETITE_CAPS:
g_string_append (s, "font-variant: all-petite-caps; ");
break;
case PANGO_VARIANT_UNICASE:
g_string_append (s, "font-variant: unicase; ");
break;
case PANGO_VARIANT_TITLE_CAPS:
g_string_append (s, "font-variant: titling-caps; ");
break;
default:
break;
}
}
if (set & PANGO_FONT_MASK_WEIGHT)
{
switch (pango_font_description_get_weight (desc))
{
case PANGO_WEIGHT_THIN:
g_string_append (s, "font-weight: 100; ");
break;
case PANGO_WEIGHT_ULTRALIGHT:
g_string_append (s, "font-weight: 200; ");
break;
case PANGO_WEIGHT_LIGHT:
case PANGO_WEIGHT_SEMILIGHT:
g_string_append (s, "font-weight: 300; ");
break;
case PANGO_WEIGHT_BOOK:
case PANGO_WEIGHT_NORMAL:
g_string_append (s, "font-weight: 400; ");
break;
case PANGO_WEIGHT_MEDIUM:
g_string_append (s, "font-weight: 500; ");
break;
case PANGO_WEIGHT_SEMIBOLD:
g_string_append (s, "font-weight: 600; ");
break;
case PANGO_WEIGHT_BOLD:
g_string_append (s, "font-weight: 700; ");
break;
case PANGO_WEIGHT_ULTRABOLD:
g_string_append (s, "font-weight: 800; ");
break;
case PANGO_WEIGHT_HEAVY:
case PANGO_WEIGHT_ULTRAHEAVY:
g_string_append (s, "font-weight: 900; ");
break;
default:
break;
}
}
if (set & PANGO_FONT_MASK_STRETCH)
{
switch (pango_font_description_get_stretch (desc))
{
case PANGO_STRETCH_ULTRA_CONDENSED:
g_string_append (s, "font-stretch: ultra-condensed; ");
break;
case PANGO_STRETCH_EXTRA_CONDENSED:
g_string_append (s, "font-stretch: extra-condensed; ");
break;
case PANGO_STRETCH_CONDENSED:
g_string_append (s, "font-stretch: condensed; ");
break;
case PANGO_STRETCH_SEMI_CONDENSED:
g_string_append (s, "font-stretch: semi-condensed; ");
break;
case PANGO_STRETCH_NORMAL:
g_string_append (s, "font-stretch: normal; ");
break;
case PANGO_STRETCH_SEMI_EXPANDED:
g_string_append (s, "font-stretch: semi-expanded; ");
break;
case PANGO_STRETCH_EXPANDED:
g_string_append (s, "font-stretch: expanded; ");
break;
case PANGO_STRETCH_EXTRA_EXPANDED:
g_string_append (s, "font-stretch: extra-expanded; ");
break;
case PANGO_STRETCH_ULTRA_EXPANDED:
g_string_append (s, "font-stretch: ultra-expanded; ");
break;
default:
break;
}
}
if (set & PANGO_FONT_MASK_SIZE)
{
g_string_append_printf (s, "font-size: %dpt; ", pango_font_description_get_size (desc) / PANGO_SCALE);
}
if (set & PANGO_FONT_MASK_VARIATIONS)
{
const char *variations;
g_string_append (s, "font-variation-settings: ");
variations = pango_font_description_get_variations (desc);
add_css_variations (s, variations);
g_string_append (s, "; ");
}
g_string_append (s, "}");
return g_string_free (s, FALSE);
}