Skip to content

Instantly share code, notes, and snippets.

@LadyNamedLaura
Last active December 11, 2015 03:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LadyNamedLaura/4539003 to your computer and use it in GitHub Desktop.
Save LadyNamedLaura/4539003 to your computer and use it in GitHub Desktop.
C implementation of systemd-analyze {time,blame,plot} plot.svg is an example plot produced by this tool (I have no idea why my system creates 134 tty's)
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <locale.h>
#include "install.h"
#include "log.h"
#include "dbus-common.h"
#include "build.h"
#include "util.h"
#define svg(...) printf(__VA_ARGS__)
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
struct unit_times {
char *name;
unsigned long long int ixt;
unsigned long long int iet;
unsigned long long int axt;
unsigned long long int aet;
};
unsigned long long int property_getull(
DBusConnection *bus,
const char *dest,
const char *path,
const char *interface,
const char *property)
{
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusMessageIter iter, sub;
unsigned long long int result = 0;
union {
char byte;
dbus_int16_t i16;
dbus_uint16_t u16;
dbus_int32_t i32;
dbus_uint32_t u32;
dbus_int64_t i64;
dbus_uint64_t u64;
} dbus_result;
int r = bus_method_call_with_reply (
bus,
dest,
path,
"org.freedesktop.DBus.Properties",
"Get",
&reply,
NULL,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
if (r)
goto finish;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
goto finish;
dbus_message_iter_recurse(&iter, &sub);
switch(dbus_message_iter_get_arg_type(&sub)) {
case DBUS_TYPE_BYTE:
dbus_message_iter_get_basic(&sub, &dbus_result.byte);
result = dbus_result.byte;
break;
case DBUS_TYPE_INT16:
dbus_message_iter_get_basic(&sub, &dbus_result.i16);
result = dbus_result.i16;
break;
case DBUS_TYPE_UINT16:
dbus_message_iter_get_basic(&sub, &dbus_result.u16);
result = dbus_result.u16;
break;
case DBUS_TYPE_INT32:
dbus_message_iter_get_basic(&sub, &dbus_result.i32);
result = dbus_result.i32;
break;
case DBUS_TYPE_UINT32:
dbus_message_iter_get_basic(&sub, &dbus_result.u32);
result = dbus_result.u32;
break;
case DBUS_TYPE_INT64:
dbus_message_iter_get_basic(&sub, &dbus_result.i64);
result = dbus_result.i64;
break;
case DBUS_TYPE_UINT64:
dbus_message_iter_get_basic(&sub, &dbus_result.u64);
result = dbus_result.u64;
break;
default:
goto finish;
}
finish:
return result;
}
static int compare_unit_times1(const void *a, const void *b) {
const struct unit_times *u = a, *v = b;
return (int)(v->aet - v->ixt) - (int)(u->aet - u->ixt);
}
static int compare_unit_times2(const void *a, const void *b) {
const struct unit_times *u = a, *v = b;
return (long long int)(u->ixt) - (long long int)(v->ixt);
}
int acquire_time_data(DBusConnection *bus, struct unit_times **out)
{
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusMessageIter iter, sub, sub2;
unsigned int c = 0, n_units = 0;
struct unit_times *unit_times = NULL;
int r = bus_method_call_with_reply (
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"ListUnits",
&reply,
NULL,
DBUS_TYPE_INVALID);
if (r)
goto finish;
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
dbus_message_iter_recurse(&iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
struct unit_times *u;
char *path;
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
if (c >= n_units) {
struct unit_times *w;
n_units = MAX(2*c, 16);
w = realloc(unit_times, sizeof(struct unit_times) * n_units);
if (!w) {
log_error("Failed to allocate unit array.");
r = -ENOMEM;
goto finish;
}
unit_times = w;
}
u = unit_times+c;
dbus_message_iter_recurse(&sub, &sub2);
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->name, true) < 0 ||
dbus_message_iter_next(&sub2), dbus_message_iter_next(&sub2), dbus_message_iter_next(&sub2),
dbus_message_iter_next(&sub2), dbus_message_iter_next(&sub2),
bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, true) < 0) {
log_error("Failed to parse reply.");
r = -EIO;
goto finish;
}
u->name = strdup(u->name);
u->ixt = property_getull(bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"InactiveExitTimestampMonotonic");
u->iet = property_getull(bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"InactiveEnterTimestampMonotonic");
u->axt = property_getull(bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"ActiveExitTimestampMonotonic");
u->aet = property_getull(bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"ActiveEnterTimestampMonotonic");
dbus_message_iter_next(&sub);
if (u->ixt == 0)
continue;
c++;
}
*out = unit_times;
return c;
finish:
free(unit_times);
return r;
}
static void svg_graph_box(int height, long long int begin, long long int end, float scale_x, float scale_y)
{
double d = 0.0;
int i = 0;
/* outside box, fill */
svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
0,
scale_x * (end - begin),
scale_y * height);
for (d = 0.000001 * begin; d <= 0.000001 * end;
d += 0.1) {
/* lines for each second */
if (i % 50 == 0)
svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
scale_x * d,
scale_x * d,
scale_y * height);
else if (i % 10 == 0)
svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
scale_x * d,
scale_x * d,
scale_y * height);
else
svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
scale_x * d,
scale_x * d,
scale_y * height);
/* time label */
if (i % 10 == 0)
svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
scale_x * d, -5.0, d);
i++;
}
}
int analyze_plot(DBusConnection *bus)
{
struct unit_times *times;
int n = acquire_time_data(bus, &times);
int m = n + 1;
unsigned long long int firmware_time, loader_time, kernel_time, initrd_time, userspace_time, finish_time;
long long int starttime = 0;
float scale_x = 100.0;
float scale_y = 20.0;
if (n<=0)
return -n;
qsort(times, n, sizeof(struct unit_times), compare_unit_times2);
firmware_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FirmwareTimestampMonotonic");
loader_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LoaderTimestampMonotonic");
kernel_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KernelTimestamp");
initrd_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"InitRDTimestampMonotonic");
userspace_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UserspaceTimestampMonotonic");
finish_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FinishTimestampMonotonic");
if (firmware_time > 0) {
m++;
starttime += firmware_time - loader_time;
}
if (loader_time > 0) {
m++;
starttime += loader_time;
}
if (initrd_time > 0)
m += 2;
else if (kernel_time > 0)
m++;
float width = 80.0 + (scale_x * (starttime + finish_time) * 0.000001);
float height = 150.0 + (m* scale_y);
svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
//svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ", width, height);
svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
/* write some basic info as a comment, including some help */
svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n");
svg("<!-- browser such as Chrome/Chromium, firefox. Other applications that -->\n");
svg("<!-- render these files properly but much more slow are ImageMagick, -->\n");
svg("<!-- gimp, inkscape, etc.. To display the files on your system, just -->\n");
svg("<!-- point your browser to file:///var/log/ and click. -->\n\n");
svg("<!-- this plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
/* style sheet */
svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
svg(" rect { stroke-width: 1; stroke-opacity: 0; }\n");
svg(" rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n");
svg(" rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n");
svg(" rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n");
svg(" rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n");
svg(" rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n");
svg(" rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n");
svg(" rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n");
svg(" rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n");
svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
svg("// line.sec1 { }\n");
svg(" line.sec5 { stroke-width: 2; }\n");
svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
svg(" line.dot { stroke-dasharray: 2 4; }\n");
svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
svg(" .run { font-size: 8; font-style: italic; }\n");
svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
svg(" text.sec { font-size: 8; }\n");
svg(" text.t1 { font-size: 24; }\n");
svg(" text.t2 { font-size: 12; }\n");
svg(" text.idle { font-size: 18; }\n");
svg(" ]]>\n </style>\n</defs>\n\n");
svg("<text x=\"20\" y=\"40\">Startup finished in ");
if (firmware_time > 0)
svg("%llums (firmware) + ", (firmware_time - loader_time) / 1000);
if (loader_time > 0)
svg("%llums (loader) + ", loader_time / 1000);
if (initrd_time > 0)
svg("%llums (kernel) + %llums (initrd) + ", initrd_time / 1000, (userspace_time - initrd_time) / 1000);
else if (kernel_time > 0)
svg("%llums (kernel) + ", userspace_time / 1000);
svg("%llums (userspace) ", (finish_time - userspace_time) / 1000);
if (kernel_time > 0)
svg("= %llums\n", (firmware_time + finish_time) / 1000);
else
svg("= %llums\n", (finish_time - userspace_time) / 1000);
svg("</text>");
svg("<g transform=\"translate(20,100)\">\n");
svg_graph_box(m, starttime, finish_time, scale_x, scale_y);
float top = 0.0;
if (firmware_time > 0) {
svg(" <rect class=\"firmware\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime - firmware_time) * 0.000001,
top,
scale_x * (firmware_time - loader_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">firmware</text>\n",
scale_x * (starttime - firmware_time) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
}
if (loader_time > 0) {
svg(" <rect class=\"loader\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime - loader_time) * 0.000001,
top,
scale_x * (loader_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">loader</text>\n",
scale_x * (starttime - loader_time) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
}
if (initrd_time > 0) {
svg(" <rect class=\"kernel\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime) * 0.000001,
top,
scale_x * (initrd_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">kernel</text>\n",
scale_x * (starttime) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
svg(" <rect class=\"inird\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime + initrd_time) * 0.000001,
top,
scale_x * (userspace_time - initrd_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">initrd</text>\n",
scale_x * (starttime + initrd_time) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
} else if (kernel_time > 0) {
svg(" <rect class=\"kernel\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime) * 0.000001,
top,
scale_x * (userspace_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">kernel</text>\n",
scale_x * (starttime) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
}
svg(" <rect class=\"userspace\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (userspace_time) * 0.000001,
top,
scale_x * (finish_time - userspace_time) * 0.000001,
scale_y - 1.0);
svg(" <text x=\"%.03f\" y=\"%.03f\">userspace</text>\n",
scale_x * (userspace_time) * 0.000001 + 5.0,
top + 14.0);
top += scale_y;
for (int i=0; i < n; i++) {
//draw times[i]
bool drawn = false;
if (times[i].ixt >= userspace_time && times[i].ixt <= finish_time) {
unsigned long long int end = finish_time;
if (times[i].aet >= times[i].ixt && times[i].aet < end)
end = times[i].aet;
if (times[i].axt >= times[i].ixt && times[i].axt < end)
end = times[i].axt;
if (times[i].iet >= times[i].ixt && times[i].iet < end)
end = times[i].iet;
svg(" <rect class=\"activating\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime + times[i].ixt) * 0.000001,
top + (scale_y * i),
scale_x * (end - times[i].ixt) * 0.000001,
scale_y - 1.0);
}
if (times[i].aet >= userspace_time && times[i].aet <= finish_time) {
unsigned long long int end = finish_time;
if (times[i].axt >= times[i].aet && times[i].axt < end)
end = times[i].axt;
if (times[i].iet >= times[i].aet && times[i].iet < end)
end = times[i].iet;
svg(" <rect class=\"active\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime + times[i].aet) * 0.000001,
top + (scale_y * i),
scale_x * (end - times[i].aet) * 0.000001,
scale_y - 1.0);
}
if (times[i].axt >= userspace_time && times[i].axt <= finish_time) {
unsigned long long int end = finish_time;
if (times[i].iet >= times[i].axt && times[i].iet < end)
end = times[i].iet;
svg(" <rect class=\"deactivating\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
scale_x * (starttime + times[i].axt) * 0.000001,
top + (scale_y * i),
scale_x * (end - times[i].axt) * 0.000001,
scale_y - 1.0);
}
svg(" <text x=\"%.03f\" y=\"%.03f\">%s</text>\n",
(starttime + times[i].ixt) * scale_x * 0.000001 + 5.0,
top + (scale_y * i) + 14.0,
times[i].name);
}
svg("</g>\n\n");
svg("</svg>");
return 0;
}
int analyze_blame(DBusConnection *bus)
{
struct unit_times *times;
int n = acquire_time_data(bus, &times);
if (n<=0)
return -n;
qsort(times, n, sizeof(struct unit_times), compare_unit_times1);
for (int i = 0; i < n; i++) {
if (times[i].ixt <= 0 || times[i].aet <= 0)
continue;
if (times[i].aet <= times[i].ixt)
continue;
printf("%6llums %s\n", (times[i].aet - times[i].ixt) / 1000, times[i].name);
}
return 0;
}
int analyze_time(DBusConnection *bus)
{
unsigned long long firmware_time, loader_time, kernel_time, initrd_time, userspace_time, finish_time;
firmware_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FirmwareTimestampMonotonic");
loader_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LoaderTimestampMonotonic");
kernel_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KernelTimestamp");
initrd_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"InitRDTimestampMonotonic");
userspace_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UserspaceTimestampMonotonic");
finish_time = property_getull(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FinishTimestampMonotonic");
printf("Startup finished in ");
if (firmware_time > 0)
printf("%llums (firmware) + ", (firmware_time - loader_time) / 1000);
if (loader_time > 0)
printf("%llums (loader) + ", loader_time / 1000);
if (initrd_time > 0)
printf("%llums (kernel) + %llums (initrd) + ", initrd_time / 1000, (userspace_time - initrd_time) / 1000);
else if (kernel_time > 0)
printf("%llums (kernel) + ", userspace_time / 1000);
printf("%llums (userspace) ", (finish_time - userspace_time) / 1000);
if (kernel_time > 0)
printf("= %llums\n", (firmware_time + finish_time) / 1000);
else
printf("= %llums\n", (finish_time - userspace_time) / 1000);
return 0;
}
void analyze_help()
{
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n\n"
"Commands:\n"
" time\n"
" blame\n",
program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[])
{
enum {
ARG_VERSION = 0x100,
ARG_USER,
ARG_SYSTEM
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
analyze_help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_USER:
arg_scope = UNIT_FILE_USER;
break;
case ARG_SYSTEM:
arg_scope = UNIT_FILE_SYSTEM;
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code '%c'.", c);
return -EINVAL;
}
}
return 1;
}
int main(int argc, char*argv[]) {
int r, retval = EXIT_FAILURE;
DBusConnection *bus = NULL;
DBusError error;
bool private_bus = false;
dbus_error_init(&error);
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r < 0)
goto finish;
else if (r == 0) {
retval = EXIT_SUCCESS;
goto finish;
}
bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
if (!argv[optind] || streq(argv[optind], "time"))
retval = analyze_time(bus);
else if (streq(argv[optind], "blame"))
retval = analyze_blame(bus);
else if (streq(argv[optind], "plot"))
retval = analyze_plot(bus);
else
log_error("Unknown operation '%s'.", argv[optind]);
finish:
if (bus) {
dbus_connection_flush(bus);
dbus_connection_close(bus);
dbus_connection_unref(bus);
}
dbus_error_free(&error);
return retval;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment