blob: 301f0251620b5be2cb7cde433d9e17276a7b82bb [file] [log] [blame]
/*
*
* 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(&notifier);
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)