| /* |
| * |
| * 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); |
| } |