{"action":"created","comment":{"url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/comments/60729126","id":60729126,"diff_hunk":"@@ -22,751 +18,765 @@\n  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n  * 02110-1301 USA\n  *\n+ * Contact: Dmitry Rozhkov <dmitry.rozhkov@intel.com>\n+ *\n  */\n \n-#include <stdio.h>\n+#define _GNU_SOURCE\n+\n #include <stdlib.h>\n-#include <string.h>\n-#include <signal.h>\n #include <unistd.h>\n-#include <sys/types.h>\n-\n-#include <glib.h>\n-#include <glib-unix.h>\n-#include <gio/gio.h>\n-\n-#include \"swupdd-interface.h\"\n+#include <stdio.h>\n+#include <stdbool.h>\n+#include <errno.h>\n+#include <limits.h>\n+#include <assert.h>\n+#include <fcntl.h>\n+#include <sys/wait.h>\n+#include <systemd/sd-bus.h>\n+#include <systemd/sd-daemon.h>\n+\n+#include \"log.h\"\n+#include \"list.h\"\n \n #define SWUPD_CLIENT    \"swupd\"\n-\n-\n-typedef struct _daemon_state\n-{\n-    guint busid;\n-    GDBusConnection *conn;\n-    ClrSoftwareUpdate *upif;\n-    const gchar *url;\n-    const gchar *method;\n-    GSubprocess *process;\n+#define TIMEOUT_EXIT_SEC 30\n+\n+typedef enum {\n+\tMETHOD_NOTSET = 0,\n+\tMETHOD_CHECK_UPDATE,\n+\tMETHOD_UPDATE,\n+\tMETHOD_VERIFY,\n+\tMETHOD_BUNDLE_ADD,\n+\tMETHOD_BUNDLE_REMOVE\n+} method_t;\n+\n+typedef struct _daemon_state {\n+\tsd_bus *bus;\n+\tpid_t child;\n+\tmethod_t method;\n } daemon_state_t;\n \n-\n-static const gchar *_empty_strv[] = { NULL, };\n-\n-\n-static gchar ** _list_to_strv (GList *list)\n+static const char * const _method_str_map[] = {\n+\tNULL,\n+\t\"checkUpdate\",\n+\t\"update\",\n+\t\"verify\",\n+\t\"bundleAdd\",\n+\t\"bundleRemove\"\n+};\n+\n+static const char * const _method_opt_map[] = {\n+\tNULL,\n+\t\"check-update\",\n+\t\"update\",\n+\t\"verify\",\n+\t\"bundle-add\",\n+\t\"bundle-remove\"\n+};\n+\n+static char **list_to_strv(struct list *strlist)\n {\n-    gchar **strv;\n-    gchar **temp;\n-\n-    strv = g_new0 (gchar *, g_list_length (list) + 1);\n-\n-    temp = strv;\n-    while (list)\n-    {\n-        *temp = list->data;\n-        temp++;\n-        list = g_list_next (list);\n-    }\n-    return strv;\n+\tchar **strv;\n+\tchar **temp;\n+\n+\tstrv = (char **)malloc((list_len(strlist) + 1) * sizeof(char *));\n+\tmemset(strv, 0x00, (list_len(strlist) + 1) * sizeof(char *));\n+\n+\ttemp = strv;\n+\twhile (strlist)\n+\t{\n+\t\t*temp = strlist->data;\n+\t\ttemp++;\n+\t\tstrlist = strlist->next;\n+\t}\n+\treturn strv;\n }\n \n-\n-static GList * _set_defaults (GList *head, daemon_state_t *state)\n+static int is_in_array(const char *key, char const * const arr[])\n {\n-    gboolean have_url = FALSE;\n-    GList *iter = head;\n-\n-    while (iter)\n-    {\n-        if (strncmp ((const char *) iter->data, \"--url=\", 6) == 0)\n-            have_url = TRUE;\n-        iter = g_list_next (iter);\n-    }\n-\n-    if (!have_url)\n-        head = g_list_append (head,\n-                              g_strdup_printf (\"--url=%s\", state->url));\n-\n-    return head;\n+\tif (arr == NULL) {\n+\t\treturn 0;\n+\t}\n+\n+\tchar const * const *temp = arr;\n+\twhile (*temp) {\n+\t\tif (strcmp(key, *temp) == 0) {\n+\t\t\treturn 1;\n+\t\t}\n+\t\ttemp++;\n+\t}\n+\treturn 0;\n }\n \n-\n-static void _destroy_interface (daemon_state_t *dstate)\n+static int bus_message_read_option_string(struct list **strlist,\n+\t\t\t\t\t  sd_bus_message *m,\n+\t\t\t\t\t  const char *optname)\n {\n-    if (!dstate->upif)\n-        return;\n-\n-    g_dbus_interface_skeleton_unexport (\n-        G_DBUS_INTERFACE_SKELETON (dstate->upif));\n-    g_object_unref (dstate->upif);\n-    dstate->upif = NULL;\n+\tint r = 0;\n+\tchar *option = NULL;\n+\tconst char *value;\n+\n+\tr = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, \"s\");\n+\tif (r < 0) {\n+\t\tERR(\"Failed to enter array container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\tr = sd_bus_message_read(m, \"s\", &value);\n+\tif (r < 0) {\n+\t\tERR(\"Can't read option value: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\tr = sd_bus_message_exit_container(m);\n+\tif (r < 0) {\n+\t\tERR(\"Can't exit variant container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\n+\tr = asprintf(&option, \"--%s\", optname);\n+\tif (r < 0) {\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\t*strlist = list_append_data(*strlist, option);\n+\t*strlist = list_append_data(*strlist, strdup(value));\n+\n+\treturn 0;\n }\n \n-\n-static gboolean _signal_source_cb (gpointer user_data)\n+static int bus_message_read_option_bool(struct list **strlist,\n+\t\t\t\t\t  sd_bus_message *m,\n+\t\t\t\t\t  const char *optname)\n {\n-    GMainLoop *main_loop = (GMainLoop *) user_data;\n-\n-    g_main_loop_quit (main_loop);\n-\n-    return G_SOURCE_CONTINUE;\n+\tint value;\n+\tint r;\n+\n+\tr = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, \"b\");\n+\tif (r < 0) {\n+\t\tERR(\"Failed to enter array container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\tr = sd_bus_message_read(m, \"b\", &value);\n+\tif (r < 0) {\n+\t\tERR(\"Can't read option value: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\tr = sd_bus_message_exit_container(m);\n+\tif (r < 0) {\n+\t\tERR(\"Can't exit variant container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\n+\tif (value) {\n+\t\tchar *option = NULL;\n+\n+\t\tr = asprintf(&option, \"--%s\", optname);\n+\t\tif (r < 0) {\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\t*strlist = list_append_data(*strlist, option);\n+\t}\n+\treturn 0;\n }\n \n-\n-static void _on_process_done (GObject *object,\n-                              GAsyncResult *res,\n-                              gpointer user_data)\n+static int bus_message_read_options(struct list **strlist,\n+\t                            sd_bus_message *m,\n+\t                            char const * const opts_str[],\n+\t                            char const * const opts_bool[])\n {\n-    GSubprocess *proc = G_SUBPROCESS(object);\n-    daemon_state_t *dstate = (daemon_state_t *) user_data;\n-    GError *error = NULL;\n-    gint status;\n-    gchar *outbuf = NULL, *errbuf = NULL;\n-    gchar **messagev;\n-\n-    status = g_subprocess_get_exit_status (dstate->process);\n-    if (g_subprocess_communicate_utf8_finish (proc,\n-                                              res,\n-                                              &outbuf,\n-                                              &errbuf,\n-                                              &error))\n-    {\n-        messagev = g_strsplit_set (outbuf, \"\\r\\n\", -1);\n-        g_free (outbuf);\n-    }\n-    else\n-    {\n-        gchar *errmsg[2] = { NULL, NULL };\n-        errmsg[0] = error->message;\n-        messagev = g_strdupv (errmsg);\n-        g_error_free (error);\n-    }\n-\n-    g_object_unref (dstate->process);\n-    dstate->process = NULL;\n-\n-    clr_software_update__emit_request_completed (dstate->upif,\n-                                                 dstate->method,\n-                                                 status,\n-                                                 (const gchar * const *) messagev);\n-    g_strfreev (messagev);\n+\tint r;\n+\n+\tr = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, \"{sv}\");\n+\tif (r < 0) {\n+\t\tERR(\"Failed to enter array container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\n+\twhile ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, \"sv\")) > 0) {\n+                const char *argname;\n+\n+                r = sd_bus_message_read(m, \"s\", &argname);\n+                if (r < 0) {\n+\t\t\tERR(\"Can't read argument name: %s\", strerror(-r));\n+                        return r;\n+\t\t}\n+\t\tif (is_in_array(argname, opts_str)) {\n+\t\t\tr = bus_message_read_option_string(strlist, m, argname);\n+\t\t\tif (r < 0) {\n+\t\t\t\tERR(\"Can't read option '%s': %s\", argname, strerror(-r));\n+\t\t\t\treturn r;\n+\t\t\t}\n+\t\t} else if (is_in_array(argname, opts_bool)) {\n+\t\t\tr = bus_message_read_option_bool(strlist, m, argname);\n+\t\t\tif (r < 0) {\n+\t\t\t\tERR(\"Can't read option '%s': %s\", argname, strerror(-r));\n+\t\t\t\treturn r;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tr = sd_bus_message_skip(m, \"v\");\n+\t\t\tif (r < 0) {\n+\t\t\t\tERR(\"Can't skip unwanted option value: %s\", strerror(-r));\n+\t\t\t\treturn r;\n+\t\t\t}\n+\t\t}\n+\n+                r = sd_bus_message_exit_container(m);\n+                if (r < 0) {\n+\t\t\tERR(\"Can't exit dict entry container: %s\", strerror(-r));\n+                        return r;\n+\t\t}\n+\t}\n+\tr = sd_bus_message_exit_container(m);\n+\tif (r < 0) {\n+\t\tERR(\"Can't exit array container: %s\", strerror(-r));\n+\t\treturn r;\n+\t}\n+\n+\treturn 0;\n }\n \n+static int on_name_owner_change(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {\n+\tsd_event *e = userdata;\n \n-static void _handle_common_options (gchar *key,\n-                                    GVariant *value,\n-                                    GList *args)\n-{\n-    if (g_strcmp0 (key, \"url\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup_printf (\"--url=%s\",\n-                                               g_variant_get_string (value, NULL)));\n-    }\n-    else if (g_strcmp0 (key, \"port\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup_printf (\"--port=%u\",\n-                                               g_variant_get_uint16 (value)));\n-    }\n-    else if (g_strcmp0 (key, \"contenturl\") == 0 ||\n-             g_strcmp0 (key, \"versionurl\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup_printf (\"--%s=%s\",\n-                                               key,\n-                                               g_variant_get_string (value, NULL)));\n-    }\n-    else if (g_strcmp0 (key, \"format\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup_printf (\"--format=%s\",\n-                                               g_variant_get_string (value, NULL)));\n-    }\n-    else if (g_strcmp0 (key, \"path\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup_printf (\"--path=%s\",\n-                                               g_variant_get_string (value, NULL)));\n-    }\n-    else if (g_strcmp0 (key, \"force\") == 0)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup (\"--force\"));\n-    }\n-}\n+\tassert(m);\n+\tassert(e);\n \n+\tsd_bus_close(sd_bus_message_get_bus(m));\n+\tsd_event_exit(e, 0);\n \n-static gboolean _on_bundle_add (ClrSoftwareUpdate *object,\n-                                GDBusMethodInvocation *invocation,\n-                                GVariant *arg_options,\n-                                const gchar *const *arg_bundles,\n-                                gpointer user_data)\n-{\n-    daemon_state_t *dstate = (daemon_state_t *) user_data;\n-    GError *error = NULL;\n-    GList *args = NULL;\n-    GVariantIter iter;\n-    GVariant *value;\n-    gchar *key;\n-    gchar **argv;\n-\n-    g_message (\"bundleAdd\");\n-    clr_software_update__complete_bundle_add (object, invocation,\n-                                              (dstate->process == NULL));\n-    if (dstate->process)\n-        return FALSE;\n-\n-    args = g_list_append (args, g_strdup (SWUPD_CLIENT));\n-    args = g_list_append (args, g_strdup (\"bundle-add\"));\n-\n-    g_variant_iter_init (&iter, arg_options);\n-    while (g_variant_iter_next (&iter, \"{sv}\", &key, &value))\n-    {\n-        if (g_strcmp0 (key, \"list\") == 0)\n-        {\n-            args = g_list_append (args,\n-                                  g_strdup (\"--list\"));\n-        }\n-        else\n-            _handle_common_options (key, value, args);\n-        g_free (key);\n-        g_variant_unref (value);\n-    }\n-\n-    while (*arg_bundles)\n-    {\n-        args = g_list_append (args,\n-                              g_strdup (*arg_bundles));\n-        arg_bundles++;\n-    }\n-\n-    args = _set_defaults (args, dstate);\n-    argv = _list_to_strv (args);\n-    dstate->method = \"bundleAdd\";\n-    dstate->process = g_subprocess_newv ((const gchar * const *) argv,\n-                                         G_SUBPROCESS_FLAGS_STDOUT_PIPE |\n-                                         G_SUBPROCESS_FLAGS_STDERR_MERGE,\n-                                         &error);\n-    if (dstate->process == NULL)\n-    {\n-        gchar *errmsg[2] = { NULL, NULL };\n-        errmsg[0] = error->message;\n-        clr_software_update__emit_request_completed (object,\n-                                                     dstate->method,\n-                                                     -1,\n-                                                     (const gchar * const *) errmsg);\n-        g_error_free (error);\n-    }\n-    else\n-    {\n-        g_subprocess_communicate_utf8_async (dstate->process,\n-                                             NULL,\n-                                             NULL,\n-                                             _on_process_done,\n-                                             dstate);\n-    }\n-\n-    g_free (argv);\n-    g_list_free_full (args, g_free);\n-\n-    return TRUE;\n+\treturn 1;\n }\n \n-\n-static gboolean _on_bundle_remove (ClrSoftwareUpdate *object,\n-                                   GDBusMethodInvocation *invocation,\n-                                   GVariant *arg_options,\n-                                   const gchar *arg_bundle,\n-                                   gpointer user_data)\n+static int on_childs_output(sd_event_source *s, int fd, uint32_t revents, void *userdata)\n {\n-    daemon_state_t *dstate = (daemon_state_t *) user_data;\n-    GError *error = NULL;\n-    GList *args = NULL;\n-    GVariantIter iter;\n-    GVariant *value;\n-    gchar *key;\n-    gchar **argv;\n-\n-    g_message (\"bundleRemove\");\n-    clr_software_update__complete_bundle_remove (object, invocation,\n-                                                 (dstate->process == NULL));\n-    if (dstate->process)\n-        return FALSE;\n-\n-    args = g_list_append (args, g_strdup (SWUPD_CLIENT));\n-    args = g_list_append (args, g_strdup (\"bundle-remove\"));\n-\n-    g_variant_iter_init (&iter, arg_options);\n-    while (g_variant_iter_next (&iter, \"{sv}\", &key, &value))\n-    {\n-        _handle_common_options (key, value, args);\n-        g_free (key);\n-        g_variant_unref (value);\n-    }\n-\n-    if (arg_bundle)\n-        args = g_list_append (args,\n-                              g_strdup (arg_bundle));\n-\n-    args = _set_defaults (args, dstate);\n-    argv = _list_to_strv (args);\n-    dstate->method = \"bundleRemove\";\n-    dstate->process = g_subprocess_newv ((const gchar * const *) argv,\n-                                         G_SUBPROCESS_FLAGS_STDOUT_PIPE |\n-                                         G_SUBPROCESS_FLAGS_STDERR_MERGE,\n-                                         &error);\n-    if (dstate->process == NULL)\n-    {\n-        gchar *errmsg[2] = { NULL, NULL };\n-        errmsg[0] = error->message;\n-        clr_software_update__emit_request_completed (object,\n-                                                     dstate->method,\n-                                                     -1,\n-                                                     (const gchar * const *) errmsg);\n-        g_error_free (error);\n-    }\n-    else\n-    {\n-        g_subprocess_communicate_utf8_async (dstate->process,\n-                                             NULL,\n-                                             NULL,\n-                                             _on_process_done,\n-                                             dstate);\n-    }\n-\n-    g_free (argv);\n-    g_list_free_full (args, g_free);\n-\n-    return TRUE;\n+\tdaemon_state_t *context = userdata;\n+\tint r = 0;\n+\tchar buffer[PIPE_BUF + 1];\n+\tssize_t count;\n+\tcount = read(fd, buffer, PIPE_BUF);\n+\tif (count > 0) {\n+\t\tfwrite(buffer, 1, count, stdout);\n+\t\tfflush(stdout);\n+\t\tbuffer[count] = '\\0';\n+\t\tr = sd_bus_emit_signal(context->bus,\n+\t\t\t\t       \"/org/O1/swupdd/Client\",\n+\t\t\t\t       \"org.O1.swupdd.Client\",\n+\t\t\t\t       \"childOutputReceived\", \"s\", buffer);\n+\t\tif (r < 0) {\n+\t\t\tERR(\"Failed to emit signal: %s\", strerror(-r));\n+\t\t}\n+\t} else if (count < 0) {","path":"src/swupdd-main.c","position":553,"original_position":553,"commit_id":"9f41dd794362769c8d7328852796afddb8895da5","original_commit_id":"9f41dd794362769c8d7328852796afddb8895da5","user":{"login":"rojkov","id":33377,"avatar_url":"https://avatars.githubusercontent.com/u/33377?v=3","gravatar_id":"","url":"https://api.github.com/users/rojkov","html_url":"https://github.com/rojkov","followers_url":"https://api.github.com/users/rojkov/followers","following_url":"https://api.github.com/users/rojkov/following{/other_user}","gists_url":"https://api.github.com/users/rojkov/gists{/gist_id}","starred_url":"https://api.github.com/users/rojkov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rojkov/subscriptions","organizations_url":"https://api.github.com/users/rojkov/orgs","repos_url":"https://api.github.com/users/rojkov/repos","events_url":"https://api.github.com/users/rojkov/events{/privacy}","received_events_url":"https://api.github.com/users/rojkov/received_events","type":"User","site_admin":false},"body":"I see... Thanks! Then I'll retry upon `EINTR`.","created_at":"2016-04-22T12:12:31Z","updated_at":"2016-04-22T12:12:31Z","html_url":"https://github.com/ostroproject/swupd-daemon/pull/5#discussion_r60729126","pull_request_url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5","_links":{"self":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/comments/60729126"},"html":{"href":"https://github.com/ostroproject/swupd-daemon/pull/5#discussion_r60729126"},"pull_request":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5","id":65785869,"html_url":"https://github.com/ostroproject/swupd-daemon/pull/5","diff_url":"https://github.com/ostroproject/swupd-daemon/pull/5.diff","patch_url":"https://github.com/ostroproject/swupd-daemon/pull/5.patch","issue_url":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/5","number":5,"state":"closed","locked":false,"title":"Reimplement the daemon with systemd's D-Bus API","user":{"login":"rojkov","id":33377,"avatar_url":"https://avatars.githubusercontent.com/u/33377?v=3","gravatar_id":"","url":"https://api.github.com/users/rojkov","html_url":"https://github.com/rojkov","followers_url":"https://api.github.com/users/rojkov/followers","following_url":"https://api.github.com/users/rojkov/following{/other_user}","gists_url":"https://api.github.com/users/rojkov/gists{/gist_id}","starred_url":"https://api.github.com/users/rojkov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rojkov/subscriptions","organizations_url":"https://api.github.com/users/rojkov/orgs","repos_url":"https://api.github.com/users/rojkov/repos","events_url":"https://api.github.com/users/rojkov/events{/privacy}","received_events_url":"https://api.github.com/users/rojkov/received_events","type":"User","site_admin":false},"body":"This is the initial re-implementation of the daemon that doesn't depend on GLib and uses systemd's D-Bus API.\r\n\r\nThe current problem is that it uses the code for linked lists from swupd-client as we want to merge these two projects. But swupd-client is licensed as GPLv2 and this daemon is LGPLv2. Probably makes sense to rewrite the linked lists code if the daemon stays separate from swupd-client.\r\n\r\nAlso not all options and commands (comparing to the current implementation) were implemented because I've been testing the code against not the latest swupd-client. But adding support for the missing options in commands is trivial.\r\n\r\n@barbieri could you please have a look?\r\n\r\n","created_at":"2016-04-08T13:28:37Z","updated_at":"2016-04-22T12:12:31Z","closed_at":"2016-04-12T12:40:58Z","merged_at":"2016-04-12T12:40:58Z","merge_commit_sha":"446f8ea4d3c4c1f602be3af97539067ae9e2262f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5/commits","review_comments_url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5/comments","review_comment_url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/5/comments","statuses_url":"https://api.github.com/repos/ostroproject/swupd-daemon/statuses/9f41dd794362769c8d7328852796afddb8895da5","head":{"label":"rojkov:master","ref":"master","sha":"9f41dd794362769c8d7328852796afddb8895da5","user":{"login":"rojkov","id":33377,"avatar_url":"https://avatars.githubusercontent.com/u/33377?v=3","gravatar_id":"","url":"https://api.github.com/users/rojkov","html_url":"https://github.com/rojkov","followers_url":"https://api.github.com/users/rojkov/followers","following_url":"https://api.github.com/users/rojkov/following{/other_user}","gists_url":"https://api.github.com/users/rojkov/gists{/gist_id}","starred_url":"https://api.github.com/users/rojkov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rojkov/subscriptions","organizations_url":"https://api.github.com/users/rojkov/orgs","repos_url":"https://api.github.com/users/rojkov/repos","events_url":"https://api.github.com/users/rojkov/events{/privacy}","received_events_url":"https://api.github.com/users/rojkov/received_events","type":"User","site_admin":false},"repo":{"id":55779757,"name":"swupd-daemon","full_name":"rojkov/swupd-daemon","owner":{"login":"rojkov","id":33377,"avatar_url":"https://avatars.githubusercontent.com/u/33377?v=3","gravatar_id":"","url":"https://api.github.com/users/rojkov","html_url":"https://github.com/rojkov","followers_url":"https://api.github.com/users/rojkov/followers","following_url":"https://api.github.com/users/rojkov/following{/other_user}","gists_url":"https://api.github.com/users/rojkov/gists{/gist_id}","starred_url":"https://api.github.com/users/rojkov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rojkov/subscriptions","organizations_url":"https://api.github.com/users/rojkov/orgs","repos_url":"https://api.github.com/users/rojkov/repos","events_url":"https://api.github.com/users/rojkov/events{/privacy}","received_events_url":"https://api.github.com/users/rojkov/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rojkov/swupd-daemon","description":"","fork":true,"url":"https://api.github.com/repos/rojkov/swupd-daemon","forks_url":"https://api.github.com/repos/rojkov/swupd-daemon/forks","keys_url":"https://api.github.com/repos/rojkov/swupd-daemon/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rojkov/swupd-daemon/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rojkov/swupd-daemon/teams","hooks_url":"https://api.github.com/repos/rojkov/swupd-daemon/hooks","issue_events_url":"https://api.github.com/repos/rojkov/swupd-daemon/issues/events{/number}","events_url":"https://api.github.com/repos/rojkov/swupd-daemon/events","assignees_url":"https://api.github.com/repos/rojkov/swupd-daemon/assignees{/user}","branches_url":"https://api.github.com/repos/rojkov/swupd-daemon/branches{/branch}","tags_url":"https://api.github.com/repos/rojkov/swupd-daemon/tags","blobs_url":"https://api.github.com/repos/rojkov/swupd-daemon/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rojkov/swupd-daemon/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rojkov/swupd-daemon/git/refs{/sha}","trees_url":"https://api.github.com/repos/rojkov/swupd-daemon/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rojkov/swupd-daemon/statuses/{sha}","languages_url":"https://api.github.com/repos/rojkov/swupd-daemon/languages","stargazers_url":"https://api.github.com/repos/rojkov/swupd-daemon/stargazers","contributors_url":"https://api.github.com/repos/rojkov/swupd-daemon/contributors","subscribers_url":"https://api.github.com/repos/rojkov/swupd-daemon/subscribers","subscription_url":"https://api.github.com/repos/rojkov/swupd-daemon/subscription","commits_url":"https://api.github.com/repos/rojkov/swupd-daemon/commits{/sha}","git_commits_url":"https://api.github.com/repos/rojkov/swupd-daemon/git/commits{/sha}","comments_url":"https://api.github.com/repos/rojkov/swupd-daemon/comments{/number}","issue_comment_url":"https://api.github.com/repos/rojkov/swupd-daemon/issues/comments{/number}","contents_url":"https://api.github.com/repos/rojkov/swupd-daemon/contents/{+path}","compare_url":"https://api.github.com/repos/rojkov/swupd-daemon/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rojkov/swupd-daemon/merges","archive_url":"https://api.github.com/repos/rojkov/swupd-daemon/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rojkov/swupd-daemon/downloads","issues_url":"https://api.github.com/repos/rojkov/swupd-daemon/issues{/number}","pulls_url":"https://api.github.com/repos/rojkov/swupd-daemon/pulls{/number}","milestones_url":"https://api.github.com/repos/rojkov/swupd-daemon/milestones{/number}","notifications_url":"https://api.github.com/repos/rojkov/swupd-daemon/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rojkov/swupd-daemon/labels{/name}","releases_url":"https://api.github.com/repos/rojkov/swupd-daemon/releases{/id}","deployments_url":"https://api.github.com/repos/rojkov/swupd-daemon/deployments","created_at":"2016-04-08T13:08:29Z","updated_at":"2016-04-08T13:08:30Z","pushed_at":"2016-04-20T14:45:45Z","git_url":"git://github.com/rojkov/swupd-daemon.git","ssh_url":"git@github.com:rojkov/swupd-daemon.git","clone_url":"https://github.com/rojkov/swupd-daemon.git","svn_url":"https://github.com/rojkov/swupd-daemon","homepage":null,"size":79,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ostroproject:master","ref":"master","sha":"0cca14cc3298ee804c414da7ed65863e91c171bc","user":{"login":"ostroproject","id":16235628,"avatar_url":"https://avatars.githubusercontent.com/u/16235628?v=3","gravatar_id":"","url":"https://api.github.com/users/ostroproject","html_url":"https://github.com/ostroproject","followers_url":"https://api.github.com/users/ostroproject/followers","following_url":"https://api.github.com/users/ostroproject/following{/other_user}","gists_url":"https://api.github.com/users/ostroproject/gists{/gist_id}","starred_url":"https://api.github.com/users/ostroproject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ostroproject/subscriptions","organizations_url":"https://api.github.com/users/ostroproject/orgs","repos_url":"https://api.github.com/users/ostroproject/repos","events_url":"https://api.github.com/users/ostroproject/events{/privacy}","received_events_url":"https://api.github.com/users/ostroproject/received_events","type":"Organization","site_admin":false},"repo":{"id":54541954,"name":"swupd-daemon","full_name":"ostroproject/swupd-daemon","owner":{"login":"ostroproject","id":16235628,"avatar_url":"https://avatars.githubusercontent.com/u/16235628?v=3","gravatar_id":"","url":"https://api.github.com/users/ostroproject","html_url":"https://github.com/ostroproject","followers_url":"https://api.github.com/users/ostroproject/followers","following_url":"https://api.github.com/users/ostroproject/following{/other_user}","gists_url":"https://api.github.com/users/ostroproject/gists{/gist_id}","starred_url":"https://api.github.com/users/ostroproject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ostroproject/subscriptions","organizations_url":"https://api.github.com/users/ostroproject/orgs","repos_url":"https://api.github.com/users/ostroproject/repos","events_url":"https://api.github.com/users/ostroproject/events{/privacy}","received_events_url":"https://api.github.com/users/ostroproject/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ostroproject/swupd-daemon","description":"","fork":false,"url":"https://api.github.com/repos/ostroproject/swupd-daemon","forks_url":"https://api.github.com/repos/ostroproject/swupd-daemon/forks","keys_url":"https://api.github.com/repos/ostroproject/swupd-daemon/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ostroproject/swupd-daemon/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ostroproject/swupd-daemon/teams","hooks_url":"https://api.github.com/repos/ostroproject/swupd-daemon/hooks","issue_events_url":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/events{/number}","events_url":"https://api.github.com/repos/ostroproject/swupd-daemon/events","assignees_url":"https://api.github.com/repos/ostroproject/swupd-daemon/assignees{/user}","branches_url":"https://api.github.com/repos/ostroproject/swupd-daemon/branches{/branch}","tags_url":"https://api.github.com/repos/ostroproject/swupd-daemon/tags","blobs_url":"https://api.github.com/repos/ostroproject/swupd-daemon/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ostroproject/swupd-daemon/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ostroproject/swupd-daemon/git/refs{/sha}","trees_url":"https://api.github.com/repos/ostroproject/swupd-daemon/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ostroproject/swupd-daemon/statuses/{sha}","languages_url":"https://api.github.com/repos/ostroproject/swupd-daemon/languages","stargazers_url":"https://api.github.com/repos/ostroproject/swupd-daemon/stargazers","contributors_url":"https://api.github.com/repos/ostroproject/swupd-daemon/contributors","subscribers_url":"https://api.github.com/repos/ostroproject/swupd-daemon/subscribers","subscription_url":"https://api.github.com/repos/ostroproject/swupd-daemon/subscription","commits_url":"https://api.github.com/repos/ostroproject/swupd-daemon/commits{/sha}","git_commits_url":"https://api.github.com/repos/ostroproject/swupd-daemon/git/commits{/sha}","comments_url":"https://api.github.com/repos/ostroproject/swupd-daemon/comments{/number}","issue_comment_url":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/comments{/number}","contents_url":"https://api.github.com/repos/ostroproject/swupd-daemon/contents/{+path}","compare_url":"https://api.github.com/repos/ostroproject/swupd-daemon/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ostroproject/swupd-daemon/merges","archive_url":"https://api.github.com/repos/ostroproject/swupd-daemon/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ostroproject/swupd-daemon/downloads","issues_url":"https://api.github.com/repos/ostroproject/swupd-daemon/issues{/number}","pulls_url":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls{/number}","milestones_url":"https://api.github.com/repos/ostroproject/swupd-daemon/milestones{/number}","notifications_url":"https://api.github.com/repos/ostroproject/swupd-daemon/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ostroproject/swupd-daemon/labels{/name}","releases_url":"https://api.github.com/repos/ostroproject/swupd-daemon/releases{/id}","deployments_url":"https://api.github.com/repos/ostroproject/swupd-daemon/deployments","created_at":"2016-03-23T08:11:20Z","updated_at":"2016-03-23T08:19:43Z","pushed_at":"2016-04-12T12:40:59Z","git_url":"git://github.com/ostroproject/swupd-daemon.git","ssh_url":"git@github.com:ostroproject/swupd-daemon.git","clone_url":"https://github.com/ostroproject/swupd-daemon.git","svn_url":"https://github.com/ostroproject/swupd-daemon","homepage":null,"size":40,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5"},"html":{"href":"https://github.com/ostroproject/swupd-daemon/pull/5"},"issue":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/5"},"comments":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/ostroproject/swupd-daemon/statuses/9f41dd794362769c8d7328852796afddb8895da5"}}}}