| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2015 Nest Labs, Inc. All rights reserved. |
| * |
| * Author: Andrew LeCain <alecain@nestlabs.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <poll.h> |
| |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <glib.h> |
| |
| |
| #define CONNMAN_API_SUBJECT_TO_CHANGE |
| #include <connman/plugin.h> |
| #include <connman/device.h> |
| #include <connman/network.h> |
| #include <connman/inet.h> |
| #include <connman/dbus.h> |
| #include <connman/notifier.h> |
| #include <connman/log.h> |
| #include <connman/technology.h> |
| #include <connman/service.h> |
| #include <connman/ipconfig.h> |
| |
| |
| #undef DBG |
| #define DBG(fmt, arg...) \ |
| connman_debug("%s:%s() " fmt, \ |
| __FILE__, __FUNCTION__ , ## arg); |
| |
| |
| static GList *sleep_queue; |
| static GList *wake_queue; |
| |
| typedef struct event_s{ |
| void *owner; |
| GList *q; |
| time_t valid_until; |
| }event_t; |
| |
| gboolean process_queues(gpointer user_data); |
| |
| gint owner_equals(gconstpointer a, gconstpointer b) |
| { |
| event_t *e = (event_t*) a; |
| if (e->owner == b){ |
| return 0; |
| } |
| return -1; |
| } |
| |
| gint event_compare(gconstpointer aEvent, gconstpointer bEvent) |
| { |
| event_t* a = (event_t*) aEvent; |
| event_t* b = (event_t*) bEvent; |
| return a->valid_until < b->valid_until; |
| } |
| |
| void sleep_event(void *identifier, int cansleep, time_t valid_until) |
| { |
| |
| DBG("Sleep event: can%s sleep until %d\n",cansleep ? "" : "'t" , valid_until); |
| //remove any previous entries owned by current identifier |
| GList* entry = g_list_find_custom(wake_queue, identifier, &owner_equals); |
| |
| if (entry) |
| { |
| DBG("Found entry in wake queue"); |
| free(entry->data); |
| wake_queue = g_list_delete_link(wake_queue, entry); |
| } |
| else |
| { |
| entry = g_list_find_custom(sleep_queue, identifier, &owner_equals); |
| if (entry){ |
| DBG("Found entry in sleep queue"); |
| free(entry->data); |
| sleep_queue = g_list_delete_link(sleep_queue, entry); |
| } |
| } |
| |
| DBG("pruned previous requests\n"); |
| |
| //make a new event |
| event_t *new_event = (event_t *)malloc(sizeof (event_t)); |
| if (new_event == NULL){ |
| return; |
| } |
| |
| new_event->owner = identifier; |
| new_event->valid_until = valid_until; |
| |
| DBG("inserting new request\n"); |
| if(cansleep) |
| { |
| //insert entry into sleep queue |
| new_event->q = sleep_queue; |
| sleep_queue = g_list_insert(sleep_queue, new_event, -1); |
| |
| } |
| else |
| { |
| //insert entry into wake queue |
| new_event->q = wake_queue; |
| wake_queue = g_list_insert(wake_queue, new_event, -1); |
| } |
| DBG("added new request\n"); |
| |
| process_queues(NULL); |
| } |
| |
| |
| |
| gint is_expired(gconstpointer data, gconstpointer user_data) |
| { |
| |
| |
| event_t *e = (event_t*) data; |
| if ( e && ( e->valid_until <= time(NULL))){ |
| return 0; |
| } |
| return -1; |
| } |
| |
| |
| gboolean process_queues(gpointer user_data) |
| { |
| |
| //sort queues |
| DBG("sorting queues\n"); |
| sleep_queue = g_list_sort(sleep_queue, &event_compare); |
| wake_queue = g_list_sort(wake_queue, &event_compare); |
| //go through each queue and dump anything that has expired. |
| |
| DBG("sleep q len: %d, wake q len: %d\n", g_list_length(sleep_queue), g_list_length(wake_queue)); |
| |
| DBG("cleaning out queues"); |
| GList* entry; |
| while (NULL != (entry = g_list_find_custom(sleep_queue, NULL , &is_expired))) |
| { |
| DBG("Passed sleep point%d", ((event_t*) (entry->data))->valid_until); |
| free(entry->data); |
| sleep_queue = g_list_delete_link(sleep_queue, entry); |
| } |
| while (NULL != (entry = g_list_find_custom(wake_queue, NULL, &is_expired))) |
| { |
| DBG("Passed wake point %d", ((event_t *) (entry->data))->valid_until); |
| free(entry->data); |
| wake_queue = g_list_delete_link(wake_queue, entry); |
| } |
| |
| DBG("sleep q len: %d, wake q len: %d\n", g_list_length(sleep_queue), g_list_length(wake_queue)); |
| |
| DBG("checking wake queue\n"); |
| //check wake queue for ANYTHING. |
| if (wake_queue) |
| { |
| event_t *e = wake_queue->data; |
| //we can't sleep until at least e->valid_until |
| //Stay awake and reschedule for then. |
| DBG("staying awake for %d seconds", e->valid_until-time(NULL)); |
| g_timeout_add_seconds(e->valid_until, process_queues, NULL); |
| } |
| else |
| { |
| //nothing is holding us awake-- maybe we can sleep |
| DBG("checking sleep queue\n"); |
| if (sleep_queue) |
| { |
| event_t *e = sleep_queue->data; |
| //There is a valid wake time! We can sleep til then |
| DBG("Sleeping for %d seconds", e->valid_until-time(NULL)); |
| //system_sleep_until(e->valid_until); |
| } |
| //else: indeterminiate. Probably shouldn't sleep. |
| } |
| |
| return FALSE; |
| } |
| |
| static struct connman_notifier notifier = { |
| .name = "sleepplugin", |
| .priority = CONNMAN_NOTIFIER_PRIORITY_DEFAULT, |
| .default_changed = NULL, |
| .service_add = NULL, |
| .service_remove = NULL, |
| .service_enabled = NULL, |
| .offline_mode = NULL, |
| .proxy_changed = NULL, |
| .service_state_changed = NULL, |
| .ipconfig_changed = NULL, |
| .sleep_event = &sleep_event, |
| .idle_state = NULL, |
| }; |
| |
| static int sleepplugin_init(void) |
| { |
| |
| DBG(""); |
| |
| sleep_queue = NULL; |
| wake_queue = NULL; |
| |
| connman_notifier_register(¬ifier); |
| |
| return 0; |
| } |
| |
| static void sleepplugin_exit(void) |
| { |
| g_list_free_full(sleep_queue, free); |
| g_list_free_full(wake_queue, free); |
| DBG(""); |
| |
| } |
| |
| CONNMAN_PLUGIN_DEFINE(sleepplugin, "connman sleep plugin", CONNMAN_VERSION, |
| CONNMAN_PLUGIN_PRIORITY_DEFAULT, sleepplugin_init, sleepplugin_exit) |