blob: 2499bc16fff182be47eda6cc3c07e629fcb62521 [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2011 Nest Labs, Inc. All rights reserved.
*
* Author: Grant Erickson <grant@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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <string.h>
#include <sys/resource.h>
#include <glib.h>
#include <connman/log.h>
#define CONNMAN_RLIMIT_SET(r, lp, err, label) \
do { \
err = __connman_rlimit_set(r, lp, #r); \
if (err < 0) \
goto label; \
} while (0)
struct connman_rlimit {
gboolean set;
rlim_t limit;
};
struct connman_rlimits {
#if defined(RLIMIT_AS)
struct connman_rlimit as;
#endif
struct connman_rlimit core, data, fsize, nofile, stack;
#if defined(RLIMIT_MEMLOCK)
struct connman_rlimit memlock;
#endif
#if defined(RLIMIT_NPROC)
struct connman_rlimit nproc;
#endif
};
#if defined(RLIMIT_AS)
static const char * RLIMIT_AS_KEY = "rlimit-as";
#endif
static const char * RLIMIT_CORE_KEY = "rlimit-core";
static const char * RLIMIT_DATA_KEY = "rlimit-data";
static const char * RLIMIT_FSIZE_KEY = "rlimit-fsize";
static const char * RLIMIT_NOFILE_KEY = "rlimit-nofile";
static const char * RLIMIT_STACK_KEY = "rlimit-stack";
#if defined(RLIMIT_NPROC)
static const char * RLIMIT_NPROC_KEY = "rlimit-nproc";
#endif
#if defined(RLIMIT_MEMLOCK)
static const char * RLIMIT_MEMLOCK_KEY = "rlimit-memlock";
#endif
static int set_one_rlimit(int resource, rlim_t limit, const char *name)
{
int err;
struct rlimit rl;
rl.rlim_cur = rl.rlim_max = limit;
DBG("resource %d limit %u name %s", resource, (unsigned)limit, name);
err = setrlimit(resource, &rl);
if (err < 0) {
connman_warn("setrlimit(%s, {%u, %u}) failed: %s",
name, (unsigned)limit,
(unsigned)limit,
strerror(errno));
return -errno;
}
return err;
}
static inline int __connman_rlimit_set(int resource,
const struct connman_rlimit *limit,
const char *name)
{
int err = 0;
if (limit->set)
err = set_one_rlimit(resource, limit->limit, name);
return err;
}
static int __connman_rlimits_enforce(const struct connman_rlimits *limits)
{
int err;
if (limits == NULL)
return 0;
#if defined(RLIMIT_AS)
CONNMAN_RLIMIT_SET(RLIMIT_AS, &limits->as, err, done);
#endif
CONNMAN_RLIMIT_SET(RLIMIT_CORE, &limits->core, err, done);
CONNMAN_RLIMIT_SET(RLIMIT_DATA, &limits->data, err, done);
CONNMAN_RLIMIT_SET(RLIMIT_FSIZE, &limits->fsize, err, done);
CONNMAN_RLIMIT_SET(RLIMIT_NOFILE, &limits->nofile, err, done);
CONNMAN_RLIMIT_SET(RLIMIT_STACK, &limits->stack, err, done);
#if defined(RLIMIT_MEMLOCK)
CONNMAN_RLIMIT_SET(RLIMIT_MEMLOCK, &limits->memlock, err, done);
#endif
#if defined(RLIMIT_NPROC)
CONNMAN_RLIMIT_SET(RLIMIT_NPROC, &limits->nproc, err, done);
#endif
done:
return err;
}
static void __connman_rlimit_read(GKeyFile *keyfile, const gchar *key,
struct connman_rlimit *limit)
{
const char *group = "rlimits";
gint temp;
GError *error = NULL;
if (keyfile != NULL && key != NULL && limit != NULL) {
temp = g_key_file_get_integer(keyfile, group, key, &error);
DBG("key %s temp %d error %p", key, temp, error);
if (error == NULL) {
limit->set = TRUE;
limit->limit = temp;
} else {
limit->set = FALSE;
limit->limit = 0;
g_clear_error(&error);
}
DBG("set %u limit %u", limit->set, (unsigned)limit->limit);
}
}
static GKeyFile *__connman_rlimits_open(const gchar *path)
{
GKeyFile *keyfile;
gchar *data = NULL;
gboolean result;
gsize length;
DBG("path %s", path);
result = g_file_get_contents(path, &data, &length, NULL);
keyfile = g_key_file_new();
if (result == FALSE)
goto done;
if (length > 0)
g_key_file_load_from_data(keyfile, data, length, 0, NULL);
g_free(data);
done:
DBG("keyfile %p", keyfile);
return keyfile;
}
static int __connman_rlimits_load(const gchar *path,
struct connman_rlimits *limits)
{
GKeyFile *keyfile;
if (limits == NULL)
return -EINVAL;
keyfile = __connman_rlimits_open(path);
if (keyfile == NULL)
return -EIO;
#if defined(RLIMIT_AS)
__connman_rlimit_read(keyfile, RLIMIT_AS_KEY, &limits->as);
#endif
__connman_rlimit_read(keyfile, RLIMIT_CORE_KEY, &limits->core);
__connman_rlimit_read(keyfile, RLIMIT_DATA_KEY, &limits->data);
__connman_rlimit_read(keyfile, RLIMIT_FSIZE_KEY, &limits->fsize);
__connman_rlimit_read(keyfile, RLIMIT_NOFILE_KEY, &limits->nofile);
__connman_rlimit_read(keyfile, RLIMIT_STACK_KEY, &limits->stack);
#if defined(RLIMIT_NPROC)
__connman_rlimit_read(keyfile, RLIMIT_NPROC_KEY, &limits->nproc);
#endif
#if defined(RLIMIT_MEMLOCK)
__connman_rlimit_read(keyfile, RLIMIT_MEMLOCK_KEY, &limits->memlock);
#endif
g_key_file_free(keyfile);
return 0;
}
int __connman_rlimits_init(const gchar *path)
{
struct connman_rlimits rlimits;
int err;
if (path == NULL)
return -EINVAL;
err = __connman_rlimits_load(path, &rlimits);
if (err < 0)
return err;
return __connman_rlimits_enforce(&rlimits);
}