diff options
author | taeyoung <ty317.kim@samsung.com> | 2017-01-16 11:39:48 +0900 |
---|---|---|
committer | taeyoung <ty317.kim@samsung.com> | 2017-01-16 11:42:13 +0900 |
commit | da01b82cc005cd39fcb64ab4316a290605ec40df (patch) | |
tree | 6414f82b8dc6ea9b3ff95cc90db7903b8358174e /src/libsystem | |
parent | ecd23209f31cb87e989e32a414ccd9586e21c332 (diff) | |
parent | f2bbea8b404d188528cbfcef0b40e7b7d9017f58 (diff) | |
download | libsystem-da01b82cc005cd39fcb64ab4316a290605ec40df.tar.gz libsystem-da01b82cc005cd39fcb64ab4316a290605ec40df.tar.bz2 libsystem-da01b82cc005cd39fcb64ab4316a290605ec40df.zip |
Merge branch 'master' into tizen
Change-Id: Ie83044d82e9772c3ec094a5d162cacffe4d7b9a1
Signed-off-by: taeyoung <ty317.kim@samsung.com>
Diffstat (limited to 'src/libsystem')
-rw-r--r-- | src/libsystem/.gitignore | 3 | ||||
-rw-r--r-- | src/libsystem/config-parser.c | 373 | ||||
-rw-r--r-- | src/libsystem/config-parser.h | 192 | ||||
-rw-r--r-- | src/libsystem/dbus-util.h | 63 | ||||
-rw-r--r-- | src/libsystem/exec.c | 125 | ||||
-rw-r--r-- | src/libsystem/glib-util.c | 56 | ||||
-rw-r--r-- | src/libsystem/glib-util.h | 94 | ||||
-rw-r--r-- | src/libsystem/libsystem.c | 1116 | ||||
-rw-r--r-- | src/libsystem/libsystem.h | 1428 | ||||
-rw-r--r-- | src/libsystem/libsystem.pc.in | 17 | ||||
-rw-r--r-- | src/libsystem/proc-meminfo-lookup.gperf | 67 | ||||
-rw-r--r-- | src/libsystem/proc-smaps-lookup.gperf | 70 | ||||
-rw-r--r-- | src/libsystem/proc.c | 416 | ||||
-rw-r--r-- | src/libsystem/proc.h | 437 | ||||
-rw-r--r-- | src/libsystem/time-util.c | 87 |
15 files changed, 4544 insertions, 0 deletions
diff --git a/src/libsystem/.gitignore b/src/libsystem/.gitignore new file mode 100644 index 0000000..154d18c --- /dev/null +++ b/src/libsystem/.gitignore @@ -0,0 +1,3 @@ +/libsystem.pc +/proc-meminfo-lookup.c +/proc-smaps-lookup.c
\ No newline at end of file diff --git a/src/libsystem/config-parser.c b/src/libsystem/config-parser.c new file mode 100644 index 0000000..2799f0a --- /dev/null +++ b/src/libsystem/config-parser.c @@ -0,0 +1,373 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <stdbool.h> +#include <limits.h> + +#include "libsystem.h" +#include "config-parser.h" + +#define MAX_SECTION 64 + +static int config_table_lookup( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data) { + + ConfigTableItem *t; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + for (t = table; t->lvalue; t++) { + + if (!streq(lvalue, t->lvalue)) + continue; + + if (!streq_ptr(section, t->section)) + continue; + + *func = t->cb; + *ltype = t->ltype; + *data = t->data; + return 1; + } + + return 0; +} + +/* Run the user supplied parser for an assignment */ +static int config_parse_table( + const char *filename, + unsigned line, + void *table, + const char *section, + const char *lvalue, + const char *rvalue) { + + ConfigParserCallback cb = NULL; + int ltype = 0; + void *data = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + + r = config_table_lookup(table, + section, + lvalue, + &cb, + <ype, + &data); + if (r <= 0) + return r; + + if (cb) + return cb(filename, + line, + section, + lvalue, + ltype, + rvalue, + data); + + return 0; +} + +int config_parse(const char *filename, void *table) { + + _cleanup_fclose_ FILE *f = NULL; + char *sections[MAX_SECTION] = { 0 }; + char *section = NULL, *n, *e, l[LINE_MAX]; + size_t len; + int i, r, num_section = 0; + bool already; + unsigned line = 0; + + assert(filename); + + f = fopen(filename, "r"); + if (!f) + return -errno; + + while (!feof(f)) { + _cleanup_free_ char *lvalue = NULL, *rvalue = NULL; + + if (fgets(l, LINE_MAX, f) == NULL) { + if (feof(f)) + break; + + r = -errno; + goto finish; + } + + line++; + truncate_nl(l); + + if (strchr(COMMENTS NEWLINE, *l)) + continue; + + if (*l == '[') { + len = strlen(l); + if (l[len - 1] != ']') { + r = -EBADMSG; + goto finish; + } + + n = strndup(l + 1, len - 2); + if (!n) { + r = -ENOMEM; + goto finish; + } + + already = false; + for (i = 0; i < num_section; i++) { + if (streq(n, sections[i])) { + section = sections[i]; + already = true; + free(n); + break; + } + } + + if (already) + continue; + + section = n; + sections[num_section] = n; + num_section++; + if (num_section > MAX_SECTION) { + r = -EOVERFLOW; + goto finish; + } + + continue; + } + + if (!section) + continue; + + e = strchr(l, '='); + if (e == NULL) + continue; + + r = strndup_strip(l, e - l, &lvalue); + if (r < 0) + goto finish; + + r = strdup_strip(e + 1, &rvalue); + if (r < 0) + goto finish; + + r = config_parse_table(filename, + line, + table, + section, + lvalue, + rvalue); + if (r < 0) + goto finish; + } + + r = 0; + +finish: + for (i = 0; i < num_section; i++) + if (sections[i]) + free(sections[i]); + + return r; +} + +int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(dir); + if (!d) + return errno; + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_free_ char *path = NULL; + int r; + + if (de->d_type != DT_REG) + continue; + + r = asprintf(&path, "%s/%s", dir, de->d_name); + if (r < 0) + return -ENOMEM; + + /* Do not just break loop until parse all file of + * dir. ignore return */ + (void) fp(path, data); + } + + return 0; +} + +int config_parse_int( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data) { + + int *i = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (!is_number(rvalue, strlen(rvalue))) + return -EINVAL; + + *i = atoi(rvalue); + + return 0; +} + +int config_parse_bool( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data) { + + int k; + bool *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + k = parse_boolean(rvalue); + if (k < 0) + return 0; + + *b = !!k; + return 0; +} + +int config_parse_string( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data) { + + char **s = data, *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) + n = NULL; + else { + n = strdup(rvalue); + if (!n) + return -ENOMEM; + } + + free(*s); + *s = n; + + return 0; +} + +int config_parse_bytes( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data) { + + size_t *ss = data, s = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) + s = 0; + else { + r = parse_bytes(rvalue, &s); + if (r < 0) + return r; + } + + *ss = s; + + return 0; +} + +int config_parse_percent( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data) { + + size_t *percent = data, p = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (!isempty(rvalue)) { + r = parse_percent(rvalue, &p); + if (r < 0) + return r; + } + + *percent = p; + + return 0; +} diff --git a/src/libsystem/config-parser.h b/src/libsystem/config-parser.h new file mode 100644 index 0000000..1909fe8 --- /dev/null +++ b/src/libsystem/config-parser.h @@ -0,0 +1,192 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config-parser.h + * + * ini type config file parser + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#pragma once + +#include <stdio.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Prototype for a parser for a specific configuration setting + */ +typedef int (*ConfigParserCallback)( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data); + +/** + * @brief A callback function of #config_parse_dir. + * + * @param path a parsing config file name + * @param data user data to be passed by #config_parse_dir. + */ +typedef int (*ConfigParseFunc)(const char *path, void *data); + +/** + * Wraps information for parsing a specific configuration variable, to + * be stored in a simple array + */ +typedef struct ConfigTableItem { + /** + * Section + */ + const char *section; + + /** + * Name of the variable + */ + const char *lvalue; + + /** + * Function that is called to parse the variable's value + */ + ConfigParserCallback cb; + + /** + * Distinguish different variables passed to the same callback + */ + int ltype; + + /** + * Where to store the variable's data + */ + void *data; +} ConfigTableItem; + +/** + * @brief config parser function + * + * @param filename full path of config file + * @param table a table of #ConfigTableItem to parse + * + * @return 0 on success, -errno on failure. + */ +int config_parse(const char *filename, void *table); + +/** + * @brief parse all regular config files in directory + * + * @param dir dir full path + * @param fp config parse function. + * @param data user data to be passed to config parser function + * + * @return 0 on success, -errno on failure. + */ +int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data); + + +/** + * @brief A common int type rvalue parser. + * + * @param filename a parsing config file name + * @param line a parsing config file line + * @param section a parsing config file section + * @param lvalue a parsing config file left value + * @param ltype a parsing config file left value type. (not used.) + * @param rvalue a parsing config file rvalue + * @param data user data + * + * @return 0 on success, -errno on failure. + */ +int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data); + +/** + * @brief A common boolean type rvalue parser. + * + * @param filename a parsing config file name + * @param line a parsing config file line + * @param section a parsing config file section + * @param lvalue a parsing config file left value + * @param ltype a parsing config file left value type. (not used.) + * @param rvalue a parsing config file rvalue + * @param data user data + * + * @return 0 on success, -errno on failure. + */ +int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data); + +/** + * @brief A common string type rvalue parser. + * + * @param filename a parsing config file name + * @param line a parsing config file line + * @param section a parsing config file section + * @param lvalue a parsing config file left value + * @param ltype a parsing config file left value type. (not used.) + * @param rvalue a parsing config file rvalue + * @param data user data + * + * @return 0 on success, -errno on failure. + */ +int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data); + +/** + * @brief A common byte type rvalue parser. + * + * @param filename a parsing config file name + * @param line a parsing config file line + * @param section a parsing config file section + * @param lvalue a parsing config file left value + * @param ltype a parsing config file left value type. (not used.) + * @param rvalue a parsing config file rvalue + * @param data user data + * + * @return 0 on success, -errno on failure. + */ +int config_parse_bytes(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data); + +/** + * @brief A common percent(%) type rvalue parser. The right value has + * to be end with '%'. + * + * @param filename a parsing config file name + * @param line a parsing config file line + * @param section a parsing config file section + * @param lvalue a parsing config file left value + * @param ltype a parsing config file left value type. (not used.) + * @param rvalue a parsing config file rvalue + * @param data user data + * + * @return 0 on success, -errno on failure. + */ +int config_parse_percent(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data); + +#ifdef __cplusplus +} +#endif diff --git a/src/libsystem/dbus-util.h b/src/libsystem/dbus-util.h new file mode 100644 index 0000000..6f2a953 --- /dev/null +++ b/src/libsystem/dbus-util.h @@ -0,0 +1,63 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file dbus-util.h + * + * dbus utility library + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#pragma once + +#include <dbus/dbus.h> +#include "libsystem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void __dbus_error_free(DBusError *error) { + if (error && dbus_error_is_set(error)) + dbus_error_free(error); +} + +static inline void __dbus_message_unref(DBusMessage **message) { + if (*message) + dbus_message_unref(*message); +} + +/** + * Declare value with cleanup attribute. dbus_error_free() is called + * when is going out the scope. + */ +#define _cleanup_dbus_error_free_ _cleanup_(__dbus_error_free) + +/** + * Declare value with cleanup attribute. dbus_message_unref() is + * called when is going out the scope. + */ +#define _cleanup_dbus_message_unref_ _cleanup_(__dbus_message_unref) + +#ifdef __cplusplus +} +#endif diff --git a/src/libsystem/exec.c b/src/libsystem/exec.c new file mode 100644 index 0000000..5fb49f4 --- /dev/null +++ b/src/libsystem/exec.c @@ -0,0 +1,125 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <stdint.h> +#include <stdbool.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + +#include "libsystem.h" + +static int wait_child(pid_t pid, int64_t timeout_msec, int sig) { + struct timeval start, timeout; + int status; + + if (timeout_msec < 0) + return 0; + + if (timeout_msec > 0) { + msec_to_timeval((uint64_t) timeout_msec, &timeout); + + if (gettimeofday(&start, NULL) < 0) + return -errno; + } + + for (;;) { + struct timeval current, delta; + pid_t p; + + p = waitpid(pid, &status, WNOHANG); + if (p == pid) + break; + + if (timeout_msec == 0) + continue; + + if (gettimeofday(¤t, NULL) < 0) + return -errno; + + timersub(¤t, &start, &delta); + + if (timercmp(&timeout, &delta, <)) { + (void) kill(pid, sig); + return -ETIME; + } + + usleep(100000); + } + + return WEXITSTATUS(status); +} + +int do_fork_exec_kill_redirect(char *const argv[], char * const envp[], int64_t timeout_msec, int sig, int fd, int flags) { + pid_t pid; + + assert(argv); + + pid = fork(); + if (pid < 0) + return -errno; + else if (pid == 0) { + + if (fd >= 0) { + if (flags & EXEC_REDIRECT_OUTPUT) + dup2(fd, STDOUT_FILENO); + + if (flags & EXEC_REDIRECT_ERROR) + dup2(fd, STDERR_FILENO); + } + + if (!envp) + execv(*argv, argv); + else + execvpe(*argv, argv, envp); + + _exit(EXIT_FAILURE); + } + + return wait_child(pid, timeout_msec, sig); +} + +int do_fork_exec_redirect(char *const argv[], char * const envp[], int64_t timeout_msec, int fd, int flags) { + + assert(argv); + + return do_fork_exec_kill_redirect(argv, envp, timeout_msec, SIGTERM, fd, flags); +} + +int do_fork_exec_kill(char *const argv[], char * const envp[], int64_t timeout_msec, int sig) { + + assert(argv); + + return do_fork_exec_kill_redirect(argv, envp, timeout_msec, sig, -1, EXEC_REDIRECT_NONE); +} + +int do_fork_exec(char *const argv[], char * const envp[], int64_t timeout_msec) { + + assert(argv); + + return do_fork_exec_kill(argv, envp, timeout_msec, SIGTERM); +} diff --git a/src/libsystem/glib-util.c b/src/libsystem/glib-util.c new file mode 100644 index 0000000..5866b4c --- /dev/null +++ b/src/libsystem/glib-util.c @@ -0,0 +1,56 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <assert.h> +#include <glib.h> + +static const int gerror_code_table[] = { + [G_FILE_ERROR_EXIST] = EEXIST, + [G_FILE_ERROR_ISDIR] = EISDIR, + [G_FILE_ERROR_ACCES] = EACCES, + [G_FILE_ERROR_NAMETOOLONG] = ENAMETOOLONG, + [G_FILE_ERROR_NOENT] = ENOENT, + [G_FILE_ERROR_NOTDIR] = ENOTDIR, + [G_FILE_ERROR_NXIO] = ENXIO, + [G_FILE_ERROR_NODEV] = ENODEV, + [G_FILE_ERROR_ROFS] = EROFS, + [G_FILE_ERROR_TXTBSY] = ETXTBSY, + [G_FILE_ERROR_FAULT] = EFAULT, + [G_FILE_ERROR_LOOP] = ELOOP, + [G_FILE_ERROR_NOSPC] = ENOSPC, + [G_FILE_ERROR_NOMEM] = ENOMEM, + [G_FILE_ERROR_MFILE] = EMFILE, + [G_FILE_ERROR_NFILE] = ENFILE, + [G_FILE_ERROR_BADF] = EBADF, + [G_FILE_ERROR_INVAL] = EINVAL, + [G_FILE_ERROR_PIPE] = EPIPE, + [G_FILE_ERROR_AGAIN] = EAGAIN, + [G_FILE_ERROR_INTR] = EINTR, + [G_FILE_ERROR_IO] = EIO, + [G_FILE_ERROR_PERM] = EPERM, + [G_FILE_ERROR_NOSYS] = ENOSYS, +}; + +int gerror_to_errno(GError *error) { + assert(error); + + return gerror_code_table[error->code]; +} diff --git a/src/libsystem/glib-util.h b/src/libsystem/glib-util.h new file mode 100644 index 0000000..560acc4 --- /dev/null +++ b/src/libsystem/glib-util.h @@ -0,0 +1,94 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file glib-util.h + * + * GLib utility library + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#pragma once + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iterate for each list nodes. + * + * @param c current node + * @param l list to iterate + */ +#define FOREACH_G_LIST(c, l) \ + for (c = g_list_first(l); c; c = g_list_next(c)) + +/** + * @brief Reverse iterate for each list nodes. + * + * @param c current node + * @param l list to iterate + */ +#define FOREACH_G_LIST_REVERSE(c, l) \ + for (c = g_list_last(l); c; c = g_list_previous(c)) + +/** + * @brief Iterate for each list nodes. #FOREACH_G_LIST_SAFE is similar + * with #FOREACH_G_LIST but safe for list remove. When you are + * iterating a list to remove some of list nodes, you have to use + * #FOREACH_G_LIST_SAFE for safe iteration. + * + * @param c current node + * @param n next node of current iteration, this is used for safe iteration + * @param l list to iterate + */ +#define FOREACH_G_LIST_SAFE(c, n, l) \ + for (c = g_list_first(l), n = g_list_next(c); c; c = n, n = g_list_next(c)) + +/** + * @brief Reverse iterate for each list + * nodes. #FOREACH_G_LIST_SAFE_REVERSE is similar with + * #FOREACH_G_LIST_REVERSE but safe for list remove. When you are + * iterating a list to remove some of list nodes, you have to use + * #FOREACH_G_LIST_SAFE_REVERSE for safe iteration. + * + * @param c current node + * @param p previous node of current iteration, this is used for safe iteration + * @param l list to iterate + */ +#define FOREACH_G_LIST_SAFE_REVERSE(c, p, l) \ + for (c = g_list_last(l), p = g_list_previous(c); c; c = p, p = g_list_previous(c)) + +/** + * @brief Convert GError to errno. + * + * @param error GError to convert + * + * @return converted errno. + */ +int gerror_to_errno(GError *error); + +#ifdef __cplusplus +} +#endif diff --git a/src/libsystem/libsystem.c b/src/libsystem/libsystem.c new file mode 100644 index 0000000..c8b7c58 --- /dev/null +++ b/src/libsystem/libsystem.c @@ -0,0 +1,1116 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stddef.h> +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <ctype.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <mntent.h> +#include <stdint.h> +#include <inttypes.h> + +#include "libsystem.h" + +static int _errno_old; + +#define STORE_RESET_ERRNO do { \ + _errno_old = errno; \ + errno = 0; \ +} while (0) + +#define RESTORE_ERRNO do { \ + errno = _errno_old; \ + _errno_old = 0; \ +} while (0) + +bool streq_ptr(const char *a, const char *b) { + + /* Like streq(), but tries to make sense of NULL pointers */ + + if (a && b) + return streq(a, b); + + if (!a && !b) + return true; + + return false; +} + +char *truncate_nl(char *s) { + assert(s); + + s[strcspn(s, NEWLINE)] = 0; + + return s; +} + +char *strnappend(const char *s, const char *suffix, size_t b) { + size_t a; + char *r; + + if (!s && !suffix) + return strdup(""); + + if (!s) + return strndup(suffix, b); + + if (!suffix) + return strdup(s); + + assert(s); + assert(suffix); + + a = strlen(s); + if (b > ((size_t) - 1) - a) + return NULL; + + r = new(char, a + b + 1); + if (!r) + return NULL; + + memcpy(r, s, a); + memcpy(r + a, suffix, b); + r[a + b] = 0; + + return r; +} + +char *strappend(const char *s, const char *suffix) { + return strnappend(s, suffix, suffix ? strlen(suffix) : 0); +} + +char *strstrip(char *s) { + char *e; + + /* Drops trailing whitespace. Modifies the string in + * place. Returns pointer to first non-space character */ + + s += strspn(s, WHITESPACE); + + for (e = strchr(s, 0); e > s; e--) + if (!strchr(WHITESPACE, e[-1])) + break; + + *e = 0; + + return s; +} + +int strdup_strip(const char *str, char **ret) { + char *r = NULL; + size_t s, l; + + assert(str); + assert(ret); + + s = strspn(str, WHITESPACE); + + for (l = strlen(str + s); l > 0; l--) + if (!strchr(WHITESPACE, str[s + l - 1])) + break; + + r = strndup(str + s, l); + if (!r) + return -ENOMEM; + + *ret = r; + + return 0; +} + +int strndup_strip(const char *str, size_t len, char **ret) { + char *r = NULL; + size_t s, l; + + assert(str); + assert(ret); + + s = strspn(str, WHITESPACE); + + l = strlen(str + s); + if (len > s) + l = l < len - s ? l : len - s; + else + return -EFAULT; + + for (; l > 0; l--) + if (!strchr(WHITESPACE, str[s + l - 1])) + break; + + r = strndup(str + s, l); + if (!r) + return -ENOMEM; + + *ret = r; + + return 0; +} + +bool nulstr_contains(const char *nulstr, const char *needle) { + const char *i; + + if (!nulstr) + return false; + + NULSTR_FOREACH(i, nulstr) + if (streq(i, needle)) + return true; + + return false; +} + +bool path_is_absolute(const char *p) { + + assert(p); + + return p[0] == '/'; +} + +char *path_kill_slashes(char *path) { + char *f, *t; + bool slash = false; + + /* Removes redundant inner and trailing slashes. Modifies the + * passed string in-place. + * + * ///foo///bar/ becomes /foo/bar + */ + + for (f = path, t = path; *f; f++) { + + if (*f == '/') { + slash = true; + continue; + } + + if (slash) { + slash = false; + *(t++) = '/'; + } + + *(t++) = *f; + } + + /* Special rule, if we are talking of the root directory, a + trailing slash is good */ + + if (t == path && slash) + *(t++) = '/'; + + *t = 0; + return path; +} + +char *endswith(const char *s, const char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (char *) s + sl; + + if (sl < pl) + return NULL; + + if (memcmp(s + sl - pl, postfix, pl) != 0) + return NULL; + + return (char *) s + sl - pl; +} + +int parse_boolean(const char *v) { + assert(v); + + if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || strcaseeq(v, "on")) + return 1; + else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || strcaseeq(v, "off")) + return 0; + + return -EINVAL; +} + +int parse_bytes(const char *b, size_t *s) { + _cleanup_free_ char *num = NULL; + size_t len, num_l, unit_l; + + assert(b); + + len = strlen(b); + + if (!len) + return 0; + + num_l = strspn(b, "0123456789"); + if (num_l < len - 1) + return -EINVAL; + + unit_l = strcspn(b, "BKMG"); + if (num_l != unit_l) + return -EINVAL; + + num = strndup(b, num_l); + if (!num) + return -ENOMEM; + + switch (b[len - 1]) { + case 'G': + *s = atoi(num) << 30; + break; + case 'M': + *s = atoi(num) << 20; + break; + case 'K': + *s = atoi(num) << 10; + break; + case 'B': + default: + *s = atoi(num); + break; + } + + return 0; +} + +int parse_percent(const char *string, size_t *percent) { + _cleanup_free_ char *num = NULL; + size_t len, num_len, per; + + assert(string); + assert(percent); + + len = strlen(string); + if (!len) + return 0; + + if (string[len - 1] != '%') + return -EINVAL; + + num_len = strspn(string, "0123456789"); + if (num_len < len - 1) + return -EINVAL; + + num = strndup(string, num_len); + if (!num) + return -ENOMEM; + + per = atoi(num); + if (per > 100) + return -EINVAL; + + *percent = per; + + return 0; +} + +static bool __quote_complete(char *str, size_t l, char q) { + char *s, *s2; + + assert(str); + + if (!l) + return true; + + s = strchr(str, q); + if (!s || (s - str) > l) + return true; + + s = strchr(s + 1, q); + if (!s || (s - str) > l) + return false; + + s2 = strchr(s + 1, q); + if (!s2 || (s2 - str) > l) + return true; + + return __quote_complete(s + 1, l - (s + 1 - str), q); +} + +static bool quote_complete(char *str, size_t l) { + char quotes[] = QUOTES; + int i; + + assert(str); + + if (!l) + return true; + + for (i = 0; quotes[i]; i++) { + if (!__quote_complete(str, l, quotes[i])) + return false; + } + + return true; +} + +char *split(const char *c, size_t *l, const char *separator, char **state) { + bool separator_include_quotes; + char *current; + size_t s; + + assert(c); + assert(l); + assert(separator); + assert(state); + + current = *state ? *state : (char *) c; + if (!*current || *c == 0) + return NULL; + + *l = 0; + separator_include_quotes = !!strspn(separator, QUOTES); + current += strspn(current, separator); + + while ((s = strcspn(current + *l, separator))) { + *l += s; + if (separator_include_quotes || + quote_complete(current, *l)) + break; + (*l)++; + } + + *state = current + *l; + + return (char *) current; +} + +bool is_number(const char *s, int l) { + int i; + + for (i = 0; i < l; i++) + if (!isdigit(s[i])) + return false; + + return true; +} + +static int do_copy_internal(const char *src, const char *dst, mode_t mode, bool force) { + _cleanup_close_ int rfd = -1, wfd = -1; + char buf[1024]; + ssize_t red; + int r; + + assert(src); + assert(dst); + + if (!force) { + r = access(dst, F_OK); + if (r == 0) + return -EALREADY; + else if (errno != ENOENT) + return -errno; + } + + wfd = open(dst, O_CREAT | O_WRONLY | O_TRUNC, mode); + if (wfd < 0) + return -errno; + + rfd = open(src, O_RDONLY); + if (rfd < 0) + return -errno; + + while ((red = read(rfd, buf, 1024)) > 0) + if (write(wfd, buf, red) != red) + return -errno; + + if (red < 0) + return -errno; + + return 0; +} + +int do_copy_mode(const char *src, const char *dst, mode_t mode) { + + assert(src); + assert(dst); + + return do_copy_internal(src, dst, mode, false); +} + +int do_copy_mode_force(const char *src, const char *dst, mode_t mode) { + + assert(src); + assert(dst); + + return do_copy_internal(src, dst, mode, true); +} + +int do_copy(const char *src, const char *dst) { + + assert(src); + assert(dst); + + return do_copy_internal(src, dst, 0644, false); +} + +int do_copy_force(const char *src, const char *dst) { + + assert(src); + assert(dst); + + return do_copy_internal(src, dst, 0644, true); +} + +int do_mkdir(const char *path, mode_t mode) { + char d[PATH_MAX]; + size_t s, l; + int r, p; + + assert(path); + + l = strlen(path); + + for (p = 0, s = 0; p < l; p += s + 1) { + s = strcspn(path + p, "/"); + if (!s) + continue; + + assert(PATH_MAX > p + s + 1); + + r = snprintf(d, p + s + 1, "%s", path); + if (r < 0) + return r; + + r = mkdir(d, mode); + if (r < 0 && errno != EEXIST) + return -errno; + } + + return 0; +} + +int rmdir_recursive(const char *path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r; + + assert(path); + + d = opendir(path); + if (!d) + return -errno; + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_free_ char *p = NULL; + + r = asprintf(&p, "%s/%s", path, de->d_name); + if (r < 0) + return -ENOMEM; + + if (de->d_type == DT_DIR) { + r = rmdir_recursive(p); + if (r < 0) + return r; + } else { + r = unlink(p); + if (r < 0) + return r; + } + } + + return rmdir(path); +} + +char *strdup_unquote(const char *str, const char *quotes) { + size_t l; + + assert(str); + + l = strlen(str); + if (l < 2) + return strdup(str); + + if (strchr(quotes, str[0]) && str[0] == str[l - 1]) + return strndup(str + 1, l - 2); + + return strdup(str); +} + +int write_str_to_file(FILE *f, const char *str, enum file_write_flags flags) { + int r = 0; + + assert(f); + assert(str); + + STORE_RESET_ERRNO; + + (void) fputs(str, f); + if ((flags & FILE_WRITE_NEWLINE_IF_NOT) && !endswith(str, "\n")) + (void) fputc('\n', f); + + if (flags & FILE_WRITE_WITH_FFLUSH) + (void) fflush(f); + + if (ferror(f)) + r = errno ? -errno : -EIO; + + RESTORE_ERRNO; + + return r; +} + +int write_str_to_path(const char *path, const char *str, enum file_write_flags flags) { + _cleanup_fclose_ FILE *f = NULL; + + assert(path); + assert(str); + + if (flags & FILE_WRITE_APPEND) + f = fopen(path, "ae"); + else + f = fopen(path, "we"); + if (!f) + return -errno; + + return write_str_to_file(f, str, flags); +} + +int read_one_line_from_file(FILE *f, char **line) { + char t[LINE_MAX], *c; + + assert(f); + assert(line); + + STORE_RESET_ERRNO; + + if (!fgets(t, sizeof(t), f)) { + + if (ferror(f)) { + int r; + + r = errno ? -errno : -EIO; + RESTORE_ERRNO; + return r; + } + + t[0] = 0; + } + + RESTORE_ERRNO; + + c = strdup(t); + if (!c) + return -ENOMEM; + + *line = truncate_nl(c); + + return 0; +} + +int read_one_line_from_path(const char *path, char **line) { + _cleanup_fclose_ FILE *f = NULL; + + assert(path); + assert(line); + + f = fopen(path, "re"); + if (!f) + return -errno; + + return read_one_line_from_file(f, line); +} + +#define DEFINE_WRITE_NUM_TO_FILE(type, format) \ + int write_##type##_to_file(FILE *f, \ + type##_t u, \ + enum file_write_flags flags) { \ + int r = 0; \ + \ + assert(f); \ + \ + STORE_RESET_ERRNO; \ + \ + (void) fprintf(f, format, u); \ + if (flags & FILE_WRITE_NEWLINE_IF_NOT) \ + (void) fputc('\n', f); \ + \ + if (flags & FILE_WRITE_WITH_FFLUSH) \ + (void) fflush(f); \ + \ + if (ferror(f)) \ + r = errno ? -errno : -EIO; \ + \ + RESTORE_ERRNO; \ + \ + return r; \ + } + +#define DEFINE_WRITE_NUM_TO_PATH(type) \ + int write_##type##_to_path(const char *path, \ + type##_t u, \ + enum file_write_flags flags) { \ + _cleanup_fclose_ FILE *f = NULL; \ + \ + assert(path); \ + \ + if (flags & FILE_WRITE_APPEND) \ + f = fopen(path, "ae"); \ + else \ + f = fopen(path, "we"); \ + if (!f) \ + return -errno; \ + \ + return write_##type##_to_file(f, u, flags); \ + } + +#define DEFINE_WRITE_NUM_DUAL(type, format) \ + DEFINE_WRITE_NUM_TO_FILE(type, format); \ + DEFINE_WRITE_NUM_TO_PATH(type) + +#define DEFINE_READ_NUM_FROM_FILE(type, format) \ + int read_##type##_from_file(FILE *f, type##_t *num) { \ + int r = 0; \ + \ + assert(f); \ + assert(num); \ + \ + STORE_RESET_ERRNO; \ + \ + r = fscanf(f, format, num); \ + if (r == EOF && ferror(f)) \ + r = errno ? -errno : -EOF; \ + \ + RESTORE_ERRNO; \ + \ + return r; \ + } + +#define DEFINE_READ_NUM_FROM_PATH(type) \ + int read_##type##_from_path(const char *path, type##_t *num) { \ + _cleanup_fclose_ FILE *f = NULL; \ + \ + assert(path); \ + assert(num); \ + \ + f = fopen(path, "re"); \ + if (!f) \ + return -errno; \ + \ + return read_##type##_from_file(f, num); \ + } + +#define DEFINE_READ_NUM_DUAL(type, format) \ + DEFINE_READ_NUM_FROM_FILE(type, format); \ + DEFINE_READ_NUM_FROM_PATH(type) + +#define DEFINE_READ_WRITE_NUM_DUAL(type, r_format, w_format) \ + DEFINE_READ_NUM_DUAL(type, r_format); \ + DEFINE_WRITE_NUM_DUAL(type, w_format) + +DEFINE_READ_WRITE_NUM_DUAL(int32, "%d", "%d"); +DEFINE_READ_WRITE_NUM_DUAL(uint32, "%u", "%u"); +DEFINE_READ_WRITE_NUM_DUAL(int64, "%" SCNd64, "%" PRId64); +DEFINE_READ_WRITE_NUM_DUAL(uint64, "%" SCNu64, "%" PRIu64); + +int write_int_to_file(FILE *f, int num, enum file_write_flags flags) { + + assert(f); + assert(num); + + return write_int32_to_file(f, (int32_t) num, flags); +} + +int write_int_to_path(const char *path, int num, enum file_write_flags flags) { + + assert(path); + assert(num); + + return write_int32_to_path(path, (int32_t) num, flags); +} + +int write_unsigned_int_to_file(FILE *f, unsigned int num, enum file_write_flags flags) { + + assert(f); + assert(num); + + return write_uint32_to_file(f, (uint32_t) num, flags); +} + +int write_unsigned_int_to_path(const char *path, unsigned int num, enum file_write_flags flags) { + + assert(path); + assert(num); + + return write_uint32_to_path(path, (uint32_t) num, flags); +} + +int write_long_int_to_file(FILE *f, long int num, enum file_write_flags flags) { + + assert(f); + assert(num); + +#if __WORDSIZE == 64 + return write_int64_to_file(f, (int64_t) num, flags); +#else + return write_int32_to_file(f, (int32_t) num, flags); +#endif +} + +int write_long_int_to_path(const char *path, long int num, enum file_write_flags flags) { + + assert(path); + assert(num); + +#if __WORDSIZE == 64 + return write_int64_to_path(path, (int64_t) num, flags); +#else + return write_int32_to_path(path, (int32_t) num, flags); +#endif +} + +int write_unsigned_long_int_to_file(FILE *f, unsigned long int num, enum file_write_flags flags) { + + assert(f); + assert(num); + +#if __WORDSIZE == 64 + return write_uint64_to_file(f, (uint64_t) num, flags); +#else + return write_uint32_to_file(f, (uint32_t) num, flags); +#endif +} + +int write_unsigned_long_int_to_path(const char *path, unsigned long int num, enum file_write_flags flags) { + + assert(path); + assert(num); + +#if __WORDSIZE == 64 + return write_uint64_to_path(path, (uint64_t) num, flags); +#else + return write_uint32_to_path(path, (uint32_t) num, flags); +#endif +} + +int write_long_long_int_to_file(FILE *f, long long int num, enum file_write_flags flags) { + + assert(f); + assert(num); + + return write_int64_to_file(f, (int64_t) num, flags); +} + +int write_long_long_int_to_path(const char *path, long long int num, enum file_write_flags flags) { + + assert(path); + assert(num); + + return write_int64_to_path(path, (int64_t) num, flags); +} + +int write_unsigned_long_long_int_to_file(FILE *f, unsigned long long int num, enum file_write_flags flags) { + + assert(f); + assert(num); + + return write_uint64_to_file(f, (uint64_t) num, flags); +} + +int write_unsigned_long_long_int_to_path(const char *path, unsigned long long int num, enum file_write_flags flags) { + + assert(path); + assert(num); + + return write_uint64_to_path(path, (uint64_t) num, flags); +} + +int read_int_from_file(FILE *f, int *num) { + + assert(f); + assert(num); + + return read_int32_from_file(f, (int32_t *) num); +} + +int read_int_from_path(const char *path, int *num) { + + assert(path); + assert(num); + + return read_int32_from_path(path, (int32_t *) num); +} + +int read_unsigned_int_from_file(FILE *f, unsigned int *num) { + + assert(f); + assert(num); + + return read_uint32_from_file(f, (uint32_t *) num); +} + +int read_unsigned_int_from_path(const char *path, unsigned int *num) { + + assert(path); + assert(num); + + return read_uint32_from_path(path, (uint32_t *) num); +} + +int read_long_int_from_file(FILE *f, long int *num) { + + assert(f); + assert(num); + +#if __WORDSIZE == 64 + return read_int64_from_file(f, (int64_t *) num); +#else + return read_int32_from_file(f, (int32_t *) num); +#endif +} + +int read_long_int_from_path(const char *path, long int *num) { + + assert(path); + assert(num); + +#if __WORDSIZE == 64 + return read_int64_from_path(path, (int64_t *) num); +#else + return read_int32_from_path(path, (int32_t *) num); +#endif +} + +int read_unsigned_long_int_from_file(FILE *f, unsigned long int *num) { + + assert(f); + assert(num); + +#if __WORDSIZE == 64 + return read_uint64_from_file(f, (uint64_t *) num); +#else + return read_uint32_from_file(f, (uint32_t *) num); +#endif +} + +int read_unsigned_long_int_from_path(const char *path, unsigned long int *num) { + + assert(path); + assert(num); + +#if __WORDSIZE == 64 + return read_uint64_from_path(path, (uint64_t *) num); +#else + return read_uint32_from_path(path, (uint32_t *) num); +#endif +} + +int read_long_long_int_from_file(FILE *f, long long *num) { + + assert(f); + assert(num); + + return read_int64_from_file(f, (int64_t *) num); +} + +int read_long_long_int_from_path(const char *path, long long *num) { + + assert(path); + assert(num); + + return read_int64_from_path(path, (int64_t *) num); +} + +int read_unsigned_long_long_int_from_file(FILE *f, unsigned long long *num) { + + assert(f); + assert(num); + + return read_uint64_from_file(f, (uint64_t *) num); +} + +int read_unsigned_long_long_int_from_path(const char *path, unsigned long long *num) { + + assert(path); + assert(num); + + return read_uint64_from_path(path, (uint64_t *) num); +} + +int str_to_strv(const char *str, char ***strv, const char *separator) { + char *w, *state, *p; + char **v = NULL, **new = NULL; + size_t l; + size_t i = 0; + + FOREACH_WORD_SEPARATOR(w, l, str, separator, state) { + p = strndup(w, l); + if (!p) { + free(v); + return -ENOMEM; + } + + new = (char **) realloc(v, sizeof(char *) * (i + 2)); + if (!new) { + free(p); + free(v); + p = NULL; + return -ENOMEM; + } + + v = new; + + v[i] = p; + v[i + 1] = NULL; + i++; + } + + *strv = v; + + return 0; +} + +size_t sizeof_strv(char **strv) { + size_t u = 0; + + if (!strv) + return 0; + + while(strv[u++]) + ; + + return u - 1; +} + +int strv_attach(char **first, char **second, char ***strv, bool free_second) { + char **new = NULL; + size_t n1 = 0, n2 = 0; + + assert(strv); + + if (first) + n1 = sizeof_strv(first); + + if (second) { + n2 = sizeof_strv(second); + + new = (char **) realloc(first, sizeof(char *) * (n1 + n2 + 1)); + if (!new) + return -ENOMEM; + + first = new; + + memcpy(first + n1, second, sizeof(char *) * (n2 + 1)); + + if (free_second) + free(second); + } + + *strv = first; + + return 0; +} + +void strv_free_full(char **strv) { + char **s; + + if (!strv) + return; + + FOREACH_STRV(s, strv) { + if (s && *s) { + free(*s); + *s = NULL; + } + } + + free(strv); + strv = NULL; +} + +bool isdir(const char *path) { + struct stat st; + + assert(path); + + if (lstat(path, &st) < 0) + return false; + + return S_ISDIR(st.st_mode); +} + +int touch(const char *path) { + _cleanup_fclose_ FILE *f = NULL; + + assert(path); + + f = fopen(path, "w"); + if (!f) + return -errno; + + return 0; +} + +bool mnt_is_mounted(const char *fsname, const char *dir, const char *type, const char *opts) { + struct mntent *ent; + FILE *f = NULL; + bool matched = false; + + f = setmntent("/etc/mtab", "r"); + if (!f) + return false; + + while ((ent = getmntent(f))) { + if (fsname) { + if (streq(fsname, ent->mnt_fsname)) + matched = true; + else + goto no_match; + } + + if (dir) { + if (streq(dir, ent->mnt_dir)) + matched = true; + else + goto no_match; + } + + if (type) { + if (streq(type, ent->mnt_type)) + matched = true; + else + goto no_match; + } + + if (opts) { + if (streq(opts, ent->mnt_opts)) + matched = true; + else + goto no_match; + } + + if (matched) + break; + + no_match: + matched = false; + } + + endmntent(f); + + return matched; +} diff --git a/src/libsystem/libsystem.h b/src/libsystem/libsystem.h new file mode 100644 index 0000000..4452179 --- /dev/null +++ b/src/libsystem/libsystem.h @@ -0,0 +1,1428 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file libsystem.h + * + * system utility library + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#pragma once + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <unistd.h> +#include <string.h> +#include <dirent.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Many functions have no effects except the return value and their + * return value depends only on the parameters and/or global + * variables. Such a function can be subject to common subexpression + * elimination and loop optimization just as an arithmetic operator + * would be. These functions should be declared with the attribute + * pure. + */ +#define _pure_ __attribute__ ((pure)) + +/** + * The cleanup attribute runs a function when the variable goes out of + * scope. This attribute can only be applied to auto function scope + * variables; it may not be applied to parameters or variables with + * static storage duration. The function must take one parameter, a + * pointer to a type compatible with the variable. The return value of + * the function (if any) is ignored. + */ +#define _cleanup_(x) __attribute__((cleanup(x))) + +/** + * whitespaces such like space, tab or newlines + */ +#define WHITESPACE " \t\n\r" + +/** + * newlines + */ +#define NEWLINE "\n\r" + +/** + * single or double quotes + */ +#define QUOTES "\"\'" + +/** + * comment start specifiers such like sharp(#) or semicolon(;) + */ +#define COMMENTS "#;" + +/** + * @defgroup GCC_CLEANUP_ATT_GROUP gcc cleanup attribute + * + * @{ + */ + +static inline void __cleanup_free_func(void *p) { + free(*(void**) p); +} + +static inline void __cleanup_close_func(int *fd) { + if (*fd >= 0) + close(*fd); +} + +static inline void __cleanup_fclose_func(FILE **f) { + if (*f) + fclose(*f); +} + +static inline void __cleanup_pclose_func(FILE **f) { + if (*f) + pclose(*f); +} + +static inline void __cleanup_closedir_func(DIR **d) { + if (*d) + closedir(*d); +} + +static inline const char *startswith(const char *s, const char *prefix) { + if (strncmp(s, prefix, strlen(prefix)) == 0) + return s + strlen(prefix); + return NULL; +} + +static inline bool isempty(const char *p) { + return !p || !p[0]; +} + +/** + * Declare value with cleanup attribute. free() is called when is + * going out the scope. + */ +#define _cleanup_free_ _cleanup_(__cleanup_free_func) + +/** + * Declare value with cleanup attribute. close() is called when is + * going out the scope. + */ +#define _cleanup_close_ _cleanup_(__cleanup_close_func) + +/** + * Declare value with cleanup attribute. fclose() is called when is + * going out the scope. + */ +#define _cleanup_fclose_ _cleanup_(__cleanup_fclose_func) + +/** + * Declare value with cleanup attribute. pclose() is called when is + * going out the scope. + */ +#define _cleanup_pclose_ _cleanup_(__cleanup_pclose_func) + +/** + * Declare value with cleanup attribute. closedir() is called when is + * going out the scope. + */ +#define _cleanup_closedir_ _cleanup_(__cleanup_closedir_func) +/** + * @} + */ + +/** + * Allocate n number of size t memory. + */ +#define new(t, n) ((t*) malloc(sizeof(t) * (n))) + +/** + * Allocate n number of size t memory. And initialize to 0 all. + */ +#define new0(t, n) ((t*) calloc((n), sizeof(t))) + +/** + * Allocate n number memory. + */ +#define malloc0(n) (calloc((n), 1)) + +/** + * @brief Parse boolean type string. + * + * @param v String to parse. + * + * @return TRUE on "1", 'y', 'Y', 't', 'T' and "on". FALSE on "0", + * 'n', 'N', 'f', 'F', "off". + */ +int parse_boolean(const char *v) _pure_; + +/** + * @brief Parse byte type string. + * + * @param b Byte string. This can be only digit number with byte unit + * "BKMG". B is byte, K is kilo byte, M is mega byte and G is gira + * byte. Byte is default. + * @param s Parsed byte size is filled. + * + * @return 0 on success, -errno on failure. + */ +int parse_bytes(const char *b, size_t *s) _pure_; + +/** + * @brief Parse percentage type string. + * + * @param string Percentage string to parse. Such like "70%". + * @param percent Parsed percentage size is filled. + * + * @return 0 on success, -errno on failure. + */ +int parse_percent(const char *string, size_t *percent) _pure_; + +/** + * @brief check the path string is started with '/' + * + * @param p a path to check + * + * @return true if p started with '/', otherwise false. + */ +bool path_is_absolute(const char *p); + +/** + * @brief Removes redundant inner and trailing slashes. Modifies the + * passed string in-place. For example, if "///foo//bar/" is given + * then the path will be changed as "/foo/bar" + * + * @param path a path to modify. + * + * @return modified path pointer. It maybe identical with given path. + */ +char *path_kill_slashes(char *path); + +/** + * Get element number of array. + */ +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +/** + * Iterate for each struct reference. + */ +#define FOREACH_STRUCT_REF(s, f, i) \ + for ((i) = 0; s[(i)].f != NULL; (i)++) + +/** + * @brief Iterate for each directory entries exclude "." and "..". + */ +#define FOREACH_DIRENT(de, d, on_error) \ + for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ + if (!de) { \ + if (errno > 0) { \ + on_error; \ + } \ + break; \ + } else if (streq(de->d_name, ".") || \ + streq(de->d_name, "..")) \ + continue; \ + else + +/** + * @brief Check string is digit. + * + * @param s String to check. + * @param l Length to check. + * + * @return TRUE on all the characters are digit. FALSE on the others. + */ +bool is_number(const char *s, int l); + +/** + * @brief copy a file with mode, if destination file exists, return + * error. + * + * @param src source file path + * @param dst destination file path + * @param mode destination file mode + * + * @return 0 on success, -errno on failure. -EALREADY if destination + * file exist. + */ +int do_copy_mode(const char *src, const char *dst, mode_t mode); + +/** + * @brief copy a file with mode, if destination file exists, the file + * is overwritten. + * + * @param src source file path + * @param dst destination file path + * @param mode destination file mode + * + * @return 0 on success, -errno on failure. + */ +int do_copy_mode_force(const char *src, const char *dst, mode_t mode); + +/** + * @brief copy a file, destination file mode is 0644, if destination + * file exist, return error. + * + * @param src source file path + * @param dst destination file path + * + * @return 0 on success, -errno on failure. -EALREADY if destination + * file exist. + */ +int do_copy(const char *src, const char *dst); + +/** + * @brief copy a file, destination file mode is 0644, if destination + * file exist, the file is overwritten. + * + * @param src source file path + * @param dst destination file path + * + * @return 0 on success, -errno on failure. + */ +int do_copy_force(const char *src, const char *dst); + +/** + * @brief Make a directory. If parent directories are also absent, + * make them also. Corresponding with "mkdir -p". + * + * @param path Path to make directory. + * @param mode The directory mode. + * + * @return 0 on success, -errno on failure. + */ +int do_mkdir(const char *path, mode_t mode); + +/** + * @brief Remove all elements in path recursivly. + * + * @param path Path to make directory. + * + * @return 0 on success, -errno on failure. + */ +int rmdir_recursive(const char *path); + +/** + * @defgroup FILE_READ_WRITE_GROUP File Read/Write utility + * + * @{ + */ + +/** + * file write flags + */ +enum file_write_flags { + /** Append line-end(\\n) at the end of file. In case of string + * write, if given string has already line-end characters + * then this flag has no effect. */ + FILE_WRITE_NEWLINE_IF_NOT = 1 << 0, + /** Run fflush(3) after file write. */ + FILE_WRITE_WITH_FFLUSH = 1 << 1, + /** Open file as append mode. */ + FILE_WRITE_APPEND = 1 << 2, +}; + +/** + * @brief Write strings to FILE + * + * @param f File pointer. + * @param str Strings to write. + * @param flags Optional flags to write file. For + * ::FILE_WRITE_NEWLINE_IF_NOT, if str has already line-end, + * ::FILE_WRITE_NEWLINE_IF_NOT will has no effect. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_str_to_file(FILE *f, const char *str, enum file_write_flags flags); + +/** + * @brief Write strings to path. + * + * @param path File path. + * @param str Strings to write. + * @param flags Optional flags to write file. For + * ::FILE_WRITE_NEWLINE_IF_NOT, if str has already line-end, + * ::FILE_WRITE_NEWLINE_IF_NOT will has no effect. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_str_to_path(const char *path, const char *str, enum file_write_flags flags); + +/** + * @brief Write signed decimal integer to FILE. + * + * @param f File pointer. + * @param i Signed integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int32_to_file(FILE *f, int32_t i, enum file_write_flags flags); + +/** + * @brief Write signed decimal integer to path. + * + * @param path File path. + * @param i Signed integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int32_to_path(const char *path, int32_t i, enum file_write_flags flags); + +/** + * @brief Write unsigned decimal integer to FILE. + * + * @param f File pointer + * @param u Unsigned integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_uint32_to_file(FILE *f, uint32_t u, enum file_write_flags flags); + +/** + * @brief Write unsigned decimal integer to path. + * + * @param path File path. + * @param u Unsigned integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_uint32_to_path(const char *path, uint32_t u, enum file_write_flags flags); + +/** + * @brief Write 64 bit signed decimal integer to FILE. + * + * @param f File pointer. + * @param i 64 bit signed integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int64_to_file(FILE *f, int64_t i, enum file_write_flags flags); + +/** + * @brief Write 64 bit signed decimal integer to path. + * + * @param path File path. + * @param i 64 bit signed integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int64_to_path(const char *path, int64_t i, enum file_write_flags flags); + +/** + * @brief Write 64 bit unsigned decimal integer to FILE. + * + * @param f File pointer + * @param u 64 bit Unsigned integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_uint64_to_file(FILE *f, uint64_t u, enum file_write_flags flags); + +/** + * @brief Write 64 bit unsigned decimal integer to path. + * + * @param path File path. + * @param u 64 bit Unsigned integer to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_uint64_to_path(const char *path, uint64_t u, enum file_write_flags flags); + +/** + * @brief Write int to FILE. + * + * @param f File pointer + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int_to_file(FILE *f, int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_int_to_path(const char *path, int num, enum file_write_flags flags); + +/** + * @brief Write type unsigned int to FILE. + * + * @param f File pointer + * @param num unsigned int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_int_to_file(FILE *f, unsigned int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_int_to_path(const char *path, unsigned int num, enum file_write_flags flags); + +/** + * @brief Write long int to FILE. + * + * @param f File pointer + * @param num long int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_long_int_to_file(FILE *f, long int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_long_int_to_path(const char *path, long int num, enum file_write_flags flags); + +/** + * @brief Write unsigned long int to FILE. + * + * @param f File pointer + * @param num unsigned long int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_long_int_to_file(FILE *f, unsigned long int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_long_int_to_path(const char *path, unsigned long int num, enum file_write_flags flags); + +/** + * @brief Write long long int to FILE. + * + * @param f File pointer + * @param num long long int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_long_long_int_to_file(FILE *f, long long int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_long_long_int_to_path(const char *path, long long int num, enum file_write_flags flags); + +/** + * @brief Write unsigned long long int to FILE. + * + * @param f File pointer + * @param num unsigned long long int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_long_long_int_to_file(FILE *f, unsigned long long int num, enum file_write_flags flags); + +/** + * @brief Write int to path. + * + * @param path File path. + * @param num int to write. + * @param flags Optional flags to write file. if + * ::FILE_WRITE_NEWLINE_IF_NOT is set, line-end added. For detail, see + * ::file_write_flags. + * + * @return 0 on success, -errno on failure. + */ +int write_unsigned_long_long_int_to_path(const char *path, unsigned long long int num, enum file_write_flags flags); + +/** + * @brief Read the first line from FILE + * + * @param f File pointer. + * @param line Duplicated string line is filled. This value has to + * be free-ed by caller. + * + * @return 0 on success, -errno on failure. + */ +int read_one_line_from_file(FILE *f, char **line); + +/** + * @brief Read the first line from path + * + * @param path File path. + * @param line Duplicated string line is filled. This value has to + * be free-ed by caller. + * + * @return 0 on success, -errno on failure. + */ +int read_one_line_from_path(const char *path, char **line); + +/** + * @brief Read signed decimal integer from FILE. + * + * @param f File pointer. + * @param i signed int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_int32_from_file(FILE *f, int32_t *i); + +/** + * @brief Read signed decimalinteger from path. + * + * @param path File path. + * @param i signed int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_int32_from_path(const char *path, int32_t *i); + +/** + * @brief Read unsigned decimalinteger from FILE. + * + * @param f File pointer. + * @param u unsigned int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_uint32_from_file(FILE *f, uint32_t *u); + +/** + * @brief Read unsigned decimal integer from path + * + * @param path File path. + * @param u unsigned int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_uint32_from_path(const char *path, uint32_t *u); + +/** + * @brief Read 64 bit signed decimal integer from FILE. + * + * @param f File pointer. + * @param i 64 bit signed int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_int64_from_file(FILE *f, int64_t *i); + +/** + * @brief Read 64 bit signed decimal integer from path. + * + * @param path File path. + * @param i 64 bit signed int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_int64_from_path(const char *path, int64_t *i); + +/** + * @brief Read 64 bit unsigned decimal integer from FILE. + * + * @param f File pointer. + * @param u 64 bit unsigned int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_uint64_from_file(FILE *f, uint64_t *u); + +/** + * @brief Read 64 bit unsigned decimal integer from path + * + * @param path File path. + * @param u 64 bit unsigned int value pointer. + * + * @return 0 on success, -errno on failure. + */ +int read_uint64_from_path(const char *path, uint64_t *u); + +/** + * @brief Read type int from FILE + * + * @param f File pointer. + * @param num type int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_int_from_file(FILE *f, int *num); + +/** + * @brief Read type int from path + * + * @param path File path. + * @param num type int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_int_from_path(const char *path, int *num); + +/** + * @brief Read type unsigned int from FILE + * + * @param f File pointer. + * @param num type unsigned int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_int_from_file(FILE *f, unsigned int *num); + +/** + * @brief Read type unsigned int from path + * + * @param path File path. + * @param num type unsigned int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_int_from_path(const char *path, unsigned int *num); + +/** + * @brief Read type long int from FILE + * + * @param f File pointer. + * @param num type long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_long_int_from_file(FILE *f, long int *num); + +/** + * @brief Read type long int from path + * + * @param path File path. + * @param num type long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_long_int_from_path(const char *path, long int *num); + +/** + * @brief Read type unsigned long int from FILE + * + * @param f File pointer. + * @param num type unsigned long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_long_int_from_file(FILE *f, unsigned long int *num); + +/** + * @brief Read type unsigned long int from path + * + * @param path File path. + * @param num type unsigned long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_long_int_from_path(const char *path, unsigned long int *num); + +/** + * @brief Read type long long int from FILE + * + * @param f File pointer. + * @param num type long long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_long_long_int_from_file(FILE *f, long long *num); + +/** + * @brief Read type long long int from path + * + * @param path File path. + * @param num type long long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_long_long_int_from_path(const char *path, long long *num); + +/** + * @brief Read type unsigned long long int from FILE + * + * @param f File pointer. + * @param num type unsigned long long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_long_long_int_from_file(FILE *f, unsigned long long *num); + +/** + * @brief Read type unsigned long long int from path + * + * @param path File path. + * @param num type unsigned long long int pointer + * + * @return 0 on success, -errno on failure. + */ +int read_unsigned_long_long_int_from_path(const char *path, unsigned long long *num); +/** + * @} + */ + +/** + * @defgroup STRING_GROUP String helper + * + * @{ + */ + +/** + * Compare two strings. TRUE on same, FALSE on others. + * Same with (strcmp((a),(b)) == 0) + */ +#define streq(a,b) (strcmp((a),(b)) == 0) +/** + * Compare two strings for n length. TRUE on same, FALSE on others. + * Same with (strncmp((a), (b), (n)) == 0) + */ +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) + +/** + * Compare two strings. Similar to streq() but ignore case. TRUE on + * same, FALSE on others. + * Same with (strcasecmp((a),(b)) == 0) + */ +#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) + +/** + * Compare two strings for n length. Similar to strneq() but ignore + * case. TRUE on same, FALSE on others. + * Same with (strcasecmp((a),(b)) == 0) + */ +#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) + +/** + * Iterate string in strings which include null characters. + * For example, + *\code{.c} +const char str[] = { + "foo\0" + "bar\0"; +}; + +const char *s; + +NULSTR_FOREACH(s, str) { + // do something here +} + *\endcode + */ +#define NULSTR_FOREACH(i, l) \ + for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) + +/** + * @brief Like streq(), but tries to make sense of NULL pointers. + * + * @param a String. + * @param b String. + * + * @return TRUE on same, FALSE on the others. + */ +bool streq_ptr(const char *a, const char *b) _pure_; + +/** + * @brief Truncate line end characters. + * + * @param s String to truncate. + * + * @return Result string. + */ +char *truncate_nl(char *s); + +/** + * @brief Append suffix string to sting s with size b. + * + * @param s Ahead string. + * @param suffix The second string. + * @param b suffix size to append. + * + * @return Result string. This string has to be free-ed by caller. + */ +char *strnappend(const char *s, const char *suffix, size_t b); + +/** + * @brief Append suffix string to sting s. + * + * @param s Ahead string. + * @param suffix The second string. + * + * @return Result string. This string has to be free-ed by caller. + */ +char *strappend(const char *s, const char *suffix); + +/** + * @brief Drops trailing whitespaces. + * + * @param s String. + * + * @return The pointer to the first non-space character. + */ +char *strstrip(char *s); + +/** + * @brief duplicate string without leading and trailing whitespaces + * + * @param str a target string to duplicate + * @param ret newly allocated string is filled + * + * @return 0 on success, -errno on failure. + */ +int strdup_strip(const char *str, char **ret); + +/** + * @brief duplicate string without leading and trailing whitespaces, + * duplicated string is not over given length len + * + * @param str a target string to duplicate + * @param len maxium length of duplicate + * @param ret newly allocated string is filled + * + * @return 0 on success, -errno on failure. + */ +int strndup_strip(const char *str, size_t len, char **ret); + +/** + * @brief nulstr is similar to string list but each strings ends with + * null and the strings are put at one memory buffer. For example, + * "foo" and "bar" string can be represented "foo\0bar". This function + * check nulstr is containing the needle string. + * + * @param nulstr a nulstr + * @param needle a needle string to find + * + * @return true if the needle found, otherwise false. + */ +bool nulstr_contains(const char*nulstr, const char *needle); + +/** + * @brief check a string ends with postfix pattern + * + * @param s a string to check + * @param postfix postfix string + * + * @return if s is ended with postfix string the pointer of the + * string, matched pointer of s is returned. Otherwise NULL. + */ +char* endswith(const char *s, const char *postfix); + +/** + * @brief split a string into words. This api generally is not called + * directly, #FOREACH_WORD_SEPARATOR or #FOREACH_WORD are using + * this. If separator does not include quotes then quoted words are + * assumed as single word. + * + * @param c string to split + * @param l splitted word length + * @param separator separator strings such like #WHITESPACE + * @param state a state internally used + * + * @return a splitted current word pointer + */ +char *split(const char *c, size_t *l, const char *separator, char **state); + +/** + * @brief Iterate for each words. If separator does not include quotes + * then quoted words are assumed as single word. + * + * @param word Each word + * @param length Length of word + * @param s Target string + * @param separator Seperator string + * @param state Used only internal split(). + */ +#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ + for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state))) + +/** + * @brief Iterate for each words. (Seperators are WHITESPACES.) Quoted + * words are assumed as single word. + * + * @param word Each word + * @param length Length of word + * @param s Target string + * @param state Used only internal split(). + */ +#define FOREACH_WORD(word, length, s, state) \ + FOREACH_WORD_SEPARATOR(word, length, s, WHITESPACE, state) + +/** + * @brief Duplicate string and strip quotes from the string. + * + * @param str String to duplicate. + * @param quotes Quote characters to strip. Predefined #QUOTES can be + * used to specify quote and double quote. + * + * @return Result string. This value has to be free-ed by caller. + */ +char *strdup_unquote(const char *str, const char *quotes); +/** + * @} + */ + +/** + * @defgroup STRV_GROUP String List + * + * @{ + */ + +/** + * iterate for each elements of string list. + */ +#define FOREACH_STRV(s, l) \ + for ((s) = (l); (s) && *(s); (s)++) + +/** + * @brief Split given string to string list with separator. + * + * @param str string to split as string list. + * @param strv Splitted string list is filled. This string list has to + * be free-ed. + * @param separator sperators to split the string. + * + * @return 0 on success, -errno on failure. + */ +int str_to_strv(const char *str, char ***strv, const char *separator); + +/** + * @brief Get elements of string list. #sizeof_strv() does not count + * end of list NULL. For example, for {"foo", "bar", NULL} string + * list, #sizeof_strv() returns 2. + * + * @param strv string list. + * + * @return number of string list. + */ +size_t sizeof_strv(char **strv); + +/** + * @brief Merge two string lists. If {"foo", "bar"} and {"baz", "qux"} + * are given, the result is {"foo", "bar", "baz", "quz"}. + * + * @param first The first string list. + * @param second The second string list. + * @param strv Merged string list. + * @param free_second If TRUE is given, the second string list will be + * free-ed. If FALSE, no action. + * + * @return number of string list. + */ +int strv_attach(char **first, char **second, char ***strv, bool free_second); + +/** + * @brief Free all given string list + * + * @param strv string list to free. + */ +void strv_free_full(char **strv); +/** + * @} + */ + +/** + * @brief Check given path is directory or not + * + * @param path path to check + * + * @return TRUE if path is directory, FALSE on others. + */ +bool isdir(const char *path); + +/** + * @brief Simple file create api similar to touch(1) + * + * @param path file path + * + * @return 0 on success, -errno on failure. + */ +int touch(const char *path); + +/** + * @brief Check mount entry. Multiple matches of conditoin are able to + * be set with mnt_fsname, mnt_dir, mnt_type or mnt_opts. If multiple + * matches are given, return true if a entry satisfied all matches. + * + * \code{.c} +// check cgroup is mounted +if (is_mounted("cgroup", NULL, NULL, NULL)) + printf("cgroup is mounted\n"); + +// check /tmp is mounted +if (is_mounted("tmpfs", "/tmp", NULL, NULL)) + printf("/tmp is mounted\n"); + +// check cgroup is mounted as cgroup2 +if (is_mounted("cgroup", "/sys/fs/cgroup", "cgroup2", NULL)) + printf("cgroup is mounted as cgroup2\n"); + * \endcode + * + * @param fsname find matched mount filesystem name + * @param dir find matched mount dir(path) name + * @param type find matched mount type name + * @param opts find matched mount option name + * + * @return true if matched mount entry found, otherwise false. + */ +bool mnt_is_mounted(const char *fsname, const char *dir, const char *type, const char *opts); + +/** + * @defgroup EXEC_GROUP exec group + * + * @brief fork() and exec() utility + * @{ + */ + +/** + * standard output/error redirect flags + */ +enum { + /** + * Do not redirect standard output/error + */ + EXEC_REDIRECT_NONE = 0x01 << 0, + /** + * Redirect standard output only + */ + EXEC_REDIRECT_OUTPUT = 0x01 << 1, + /** + * Redirect standard error only + */ + EXEC_REDIRECT_ERROR = 0x01 << 2, + /** + * Redirect standard output and error all + */ + EXEC_REDIRECT_ALL = (EXEC_REDIRECT_OUTPUT | EXEC_REDIRECT_ERROR), +}; + +/** + * @brief Traditional fork() and exec() helper. If child is not + * deactivated within given \p timeout_msec then kill it with given + * signal. And additionally redirect child process standard output or + * standard error to given fd. + * + * @param argv array of pointers to null-terminated strings that + * represent the argument list available to the new program. The first + * argument should point to the filename associated with the file + * being executed. The array of pointers must be terminated by a NULL + * pointer. + * @param envp specify the environment of the executed program via the + * argument envp. The envp argument is an array of pointers to + * null-terminated strings and must be terminated by a NULL pointer. + * @param timeout_msec timeout millisecond to prevent infinite + * waiting. If negative is given, the parent will not wait the + * child. In other word, the parent will return immediately. If 0 is + * given, parent will wait the child infinitly. And if positive value + * is given parent will wait given milliseconds and expired return + * -1. If the child is exit within the tiemout millisecond return with + * child exit code. + * @param sig signal to kill the child on timeout. + * @param fd file descriptor to redirect child standard output or + * error. + * @param flags redirect flag. This flags is able to include + * EXEC_REDIRECT_OUTPUT or EXEC_REDIRECT_ERROR. + * + * @return exit code of child. It is fully depend on the child + * process. If the child exit with 1 then this function also return 1. + * Negative errno on error. -ETIME on timer expired. + */ +int do_fork_exec_kill_redirect(char *const argv[], char * const envp[], int64_t timeout_msec, int sig, int fd, int flags); + +/** + * @brief Traditional fork() and exec() helper. And additionally + * redirect child process standard output or standard error to given fd. + * + * @param argv array of pointers to null-terminated strings that + * represent the argument list available to the new program. The first + * argument should point to the filename associated with the file + * being executed. The array of pointers must be terminated by a NULL pointer. + * @param envp specify the environment of the executed program via the + * argument envp. The envp argument is an array of pointers to + * null-terminated strings and must be terminated by a NULL pointer. + * @param timeout_msec timeout millisecond to prevent infinite + * waiting. If negative is given, the parent will not wait the + * child. In other word, the parent will return immediately. If 0 is + * given, parent will wait the child infinitly. And if positive value + * is given parent will wait given milliseconds and expired return + * -1. If the child is exit within the tiemout millisecond return with + * child exit code. + * @param fd file descriptor to redirect child standard output or error. + * @param flags redirect flag. This flags is able to include + * EXEC_REDIRECT_OUTPUT or EXEC_REDIRECT_ERROR. + * + * @return exit code of child. It is fully depend on the child + * process. If the child exit with 1 then this function also return 1. + * Negative errno on error. -ETIME on timer expired. + */ +int do_fork_exec_redirect(char *const argv[], char * const envp[], int64_t timeout_msec, int fd, int flags); + +/** + * @brief Traditional fork() and exec() helper. If child is not + * deactivated within given \p timeout_msec then kill it with given + * signal. + * + * @param argv array of pointers to null-terminated strings that + * represent the argument list available to the new program. The first + * argument should point to the filename associated with the file + * being executed. The array of pointers must be terminated by a NULL pointer. + * @param envp specify the environment of the executed program via the + * argument envp. The envp argument is an array of pointers to + * null-terminated strings and must be terminated by a NULL pointer. + * @param timeout_msec timeout millisecond to prevent infinite + * waiting. If negative is given, the parent will not wait the + * child. In other word, the parent will return immediately. If 0 is + * given, parent will wait the child infinitly. And if positive value + * is given parent will wait given milliseconds and expired return + * -1. If the child is exit within the tiemout millisecond return with + * child exit code. + * @param sig signal to kill the child on timeout. + * + * @return exit code of child. It is fully depend on the child + * process. If the child exit with 1 then this function also return 1. + * Negative errno on error. -ETIME on timer expired. + */ +int do_fork_exec_kill(char *const argv[], char * const envp[], int64_t timeout_msec, int sig); + +/** + * @brief Traditional fork() and exec() helper. + * + * @param argv array of pointers to null-terminated strings that + * represent the argument list available to the new program. The first + * argument should point to the filename associated with the file + * being executed. The array of pointers must be terminated by a NULL pointer. + * @param envp specify the environment of the executed program via the + * argument envp. The envp argument is an array of pointers to + * null-terminated strings and must be terminated by a NULL pointer. + * @param timeout_msec timeout millisecond to prevent infinite + * waiting. If negative is given, the parent will not wait the + * child. In other word, the parent will return immediately. If 0 is + * given, parent will wait the child infinitly. And if positive value + * is given parent will wait given milliseconds and expired return + * -1. If the child is exit within the tiemout millisecond return with + * child exit code. + * + * @return exit code of child. It is fully depend on the child + * process. If the child exit with 1 then this function also return 1. + * Negative errno on error. -ETIME on timer expired. + */ +int do_fork_exec(char *const argv[], char * const envp[], int64_t timeout_msec); + +/** + * @} + */ + +/** + * @defgroup TIME_UTIL_GROUP time util group + * + * @brief time utility libraries + * @{ + */ + +/** millisecond per second */ +#define MSEC_PER_SEC 1000ULL +/** microsecond per second */ +#define USEC_PER_SEC ((uint64_t) 1000000ULL) +/** microsecond per millisecond */ +#define USEC_PER_MSEC ((uint64_t) 1000ULL) +/** nanosecond per second */ +#define NSEC_PER_SEC ((uint64_t) 1000000000ULL) +/** nanosecond per microsecond */ +#define NSEC_PER_MSEC ((uint64_t) 1000000ULL) +/** nanosecond per microsecond */ +#define NSEC_PER_USEC ((uint64_t) 1000ULL) + +/** microsecond per minute */ +#define USEC_PER_MINUTE ((uint64_t) (60ULL*USEC_PER_SEC)) +/** nanosecond per minute */ +#define NSEC_PER_MINUTE ((uint64_t) (60ULL*NSEC_PER_SEC)) +/** microsecond per hour */ +#define USEC_PER_HOUR ((uint64_t) (60ULL*USEC_PER_MINUTE)) +/** nanosecond per hour */ +#define NSEC_PER_HOUR ((uint64_t) (60ULL*NSEC_PER_MINUTE)) +/** microsecond per day */ +#define USEC_PER_DAY ((uint64_t) (24ULL*USEC_PER_HOUR)) +/** nanosecond per day */ +#define NSEC_PER_DAY ((uint64_t) (24ULL*NSEC_PER_HOUR)) +/** microsecond per week */ +#define USEC_PER_WEEK ((uint64_t) (7ULL*USEC_PER_DAY)) +/** nanosecond per week */ +#define NSEC_PER_WEEK ((uint64_t) (7ULL*NSEC_PER_DAY)) +/** microsecond per month */ +#define USEC_PER_MONTH ((uint64_t) (2629800ULL*USEC_PER_SEC)) +/** nanosecond per month */ +#define NSEC_PER_MONTH ((uint64_t) (2629800ULL*NSEC_PER_SEC)) +/** microsecond per year */ +#define USEC_PER_YEAR ((uint64_t) (31557600ULL*USEC_PER_SEC)) +/** nanosecond per year */ +#define NSEC_PER_YEAR ((uint64_t) (31557600ULL*NSEC_PER_SEC)) + +/** frequently used time format string: 12:34 */ +#define HH_MM "%H:%M" +/** frequently used time format string: 12:34:56 */ +#define HH_MM_SS "%H:%M:%S" + +/** frequently used time format string: 2015-01-23 */ +#define YYYY_MM_DD "%Y-%m-%d" +/** frequently used time format string: 2015-01-23 12:34 */ +#define YYYY_MM_DD_HH_MM "%Y-%m-%d %H:%M" +/** frequently used time format string: 2015-01-23 12:34:56 */ +#define YYYY_MM_DD_HH_MM_SS "%Y-%m-%d %H:%M:%S" +/** frequently used time format string: 2015-01-23 12:34:56 KST */ +#define YYYY_MM_DD_HH_MM_SS_Z "%Y-%m-%d %H:%M:%S %Z" + +/** frequently used time format string: Fri 2015-01-23 */ +#define DOW_YYYY_MM_DD "%a %Y-%m-%d" +/** frequently used time format string: Fri 2015-01-23 12:34 */ +#define DOW_YYYY_MM_DD_HH_MM "%a %Y-%m-%d %H:%M" +/** frequently used time format string: Fri 2015-01-23 12:34:56 */ +#define DOW_YYYY_MM_DD_HH_MM_SS "%a %Y-%m-%d %H:%M:%S" +/** frequently used time format string: Fri 2015-01-23 12:34:56 KST */ +#define DOW_YYYY_MM_DD_HH_MM_SS_Z "%a %Y-%m-%d %H:%M:%S %Z" + +/** + * @brief Convert time_t to given format time string. + * + * @param sec time second to convert + * @param format format string + * @param time string pointer to converted time is filled. On + * successful return, this value has to be free-ed by caller. + * + * @return 0 on success, -errno on failure. + */ +int sec_to_timestr(time_t sec, const char *format, char **time); + +/** + * @brief Convert time_t to \%a \%Y-\%m-\%d \%H:\%M:\%S \%Z format time string. + * + * @param sec time second to convert + * @param time string pointer to converted time is filled. On + * successful return, this value has to be free-ed by caller. + * + * @return 0 on success, -errno on failure. + */ +int sec_to_timestr_full(time_t sec, char **time); + +/** + * @brief Convert given format time string to time_t. + * + * @param format format string + * @param time time string to convert to time_t + * @param sec converted time_t + * + * @return 0 on success, -errno on failure. + */ +int timestr_to_sec(const char *format, const char *time, time_t *sec); + +/** + * @brief Make struct timeval from millisecond + * + * @param msec millisecond to Convert + * @param tv struct timeval to be filled + */ +void msec_to_timeval(uint64_t msec, struct timeval *tv); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif diff --git a/src/libsystem/libsystem.pc.in b/src/libsystem/libsystem.pc.in new file mode 100644 index 0000000..e942770 --- /dev/null +++ b/src/libsystem/libsystem.pc.in @@ -0,0 +1,17 @@ +# Package Information for pkg-config +# +# Copyright (c) 2016 Samsung Electronics Co., Ltd. +# All rights reserved. +# + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsystem +Description: system utility libraries +Version: @PACKAGE_VERSION@ +Requires.private: @LIBSYSTEM_PC_REQUIRES@ +Cflags: @LIBSYSTEM_PC_CFLAGS@ +Libs: @LIBSYSTEM_PC_LIBS@ diff --git a/src/libsystem/proc-meminfo-lookup.gperf b/src/libsystem/proc-meminfo-lookup.gperf new file mode 100644 index 0000000..1ca9fbe --- /dev/null +++ b/src/libsystem/proc-meminfo-lookup.gperf @@ -0,0 +1,67 @@ +%{ +#include <assert.h> +#include "proc.h" + +struct meminfo_mapping { + const char *name; + enum meminfo_id id; +}; +typedef struct meminfo_mapping meminfo_mapping; +%} +meminfo_mapping; +%language=ANSI-C +%define slot-name name +%define hash-function-name meminfo_mapping_hash +%define lookup-function-name meminfo_mapping_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +MemTotal, MEMINFO_ID_MEM_TOTAL +MemFree, MEMINFO_ID_MEM_FREE +MemAvailable, MEMINFO_ID_MEM_AVAILABLE +Buffers, MEMINFO_ID_BUFFERS +Cached, MEMINFO_ID_CACHED +SwapCached, MEMINFO_ID_SWAP_CACHED +Active, MEMINFO_ID_ACTIVE +Inactive, MEMINFO_ID_INACTIVE +Active(anon), MEMINFO_ID_ACTIVE_ANON +Inactive(anon), MEMINFO_ID_INACTIVE_ANON +Active(file), MEMINFO_ID_ACTIVE_FILE +Inactive(file), MEMINFO_ID_INACTIVE_FILE +Unevictable, MEMINFO_ID_UNEVICTABLE +Mlocked, MEMINFO_ID_MLOCKED +HighTotal, MEMINFO_ID_HIGH_TOTAL +HighFree, MEMINFO_ID_HIGH_FREE +LowTotal, MEMINFO_ID_LOW_TOTAL +LowFree, MEMINFO_ID_LOW_FREE +SwapTotal, MEMINFO_ID_SWAP_TOTAL +SwapFree, MEMINFO_ID_SWAP_FREE +Dirty, MEMINFO_ID_DIRTY +Writeback, MEMINFO_ID_WRITEBACK +AnonPages, MEMINFO_ID_ANON_PAGES +Mapped, MEMINFO_ID_MAPPED +Shmem, MEMINFO_ID_SHMEM +Slab, MEMINFO_ID_SLAB +SReclaimable, MEMINFO_ID_SRECLAIMABLE +SUnreclaim, MEMINFO_ID_SUNRECLAIM +KernelStack, MEMINFO_ID_KERNEL_STACK +PageTables, MEMINFO_ID_PAGE_TABLES +NFS_Unstable, MEMINFO_ID_NFS_UNSTABLE +Bounce, MEMINFO_ID_BOUNCE +WritebackTmp, MEMINFO_ID_WRITEBACK_TMP +CommitLimit, MEMINFO_ID_COMMIT_LIMIT +Committed_AS, MEMINFO_ID_COMMITTED_AS +VmallocTotal, MEMINFO_ID_VMALLOC_TOTAL +VmallocUsed, MEMINFO_ID_VMALLOC_USED +VmallocChunk, MEMINFO_ID_VMALLOC_CHUNK +%% +enum meminfo_id meminfo_string_to_id(const char *str) +{ + const struct meminfo_mapping *i; + + assert(str); + i = meminfo_mapping_lookup(str, strlen(str)); + return i ? i->id : MEMINFO_ID_INVALID; +} diff --git a/src/libsystem/proc-smaps-lookup.gperf b/src/libsystem/proc-smaps-lookup.gperf new file mode 100644 index 0000000..7cf82e1 --- /dev/null +++ b/src/libsystem/proc-smaps-lookup.gperf @@ -0,0 +1,70 @@ +%{ +#include <assert.h> +#include "proc.h" + +struct smap_mapping { + const char* name; + enum smap_id id; +}; +typedef struct smap_mapping smap_mapping; + +%} +smap_mapping; +%language=ANSI-C +%define slot-name name +%define hash-function-name smap_mapping_hash +%define lookup-function-name smap_mapping_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +AnonHugePages, SMAPS_ID_ANON_HUGE_PAGES +Anonymous, SMAPS_ID_ANONYMOUS +KernelPageSize, SMAPS_ID_KERNEL_PAGE_SIZE +Locked, SMAPS_ID_LOCKED +MMUPageSize, SMAPS_ID_MMU_PAGE_SIZE +PSwap, SMAPS_ID_PSWAP +Private_Clean, SMAPS_ID_PRIVATE_CLEAN +Private_Dirty, SMAPS_ID_PRIVATE_DIRTY +Pss, SMAPS_ID_PSS +Referenced, SMAPS_ID_REFERENCED +Rss, SMAPS_ID_RSS +Shared_Clean, SMAPS_ID_SHARED_CLEAN +Shared_Dirty, SMAPS_ID_SHARED_DIRTY +Size, SMAPS_ID_SIZE +Swap, SMAPS_ID_SWAP +%% +static const char* const smaps_string_lookup[SMAPS_ID_MAX] = { + [SMAPS_ID_ANON_HUGE_PAGES] = "AnonHugePages", + [SMAPS_ID_ANONYMOUS] = "Anonymous", + [SMAPS_ID_KERNEL_PAGE_SIZE] = "KernelPageSize", + [SMAPS_ID_LOCKED] = "Locked", + [SMAPS_ID_MMU_PAGE_SIZE] = "MMUPageSize", + [SMAPS_ID_PSWAP] = "PSwap", + [SMAPS_ID_PRIVATE_CLEAN] = "Private_Clean", + [SMAPS_ID_PRIVATE_DIRTY] = "Private_Dirty", + [SMAPS_ID_PSS] = "Pss", + [SMAPS_ID_REFERENCED] = "Referenced", + [SMAPS_ID_RSS] = "Rss", + [SMAPS_ID_SHARED_CLEAN] = "Shared_Clean", + [SMAPS_ID_SHARED_DIRTY] = "Shared_Dirty", + [SMAPS_ID_SIZE] = "Size", + [SMAPS_ID_SWAP] = "Swap", +}; + +const char *smap_id_to_string(enum smap_id id) { + + assert(id >= 0 && id < SMAPS_ID_MAX); + + return smaps_string_lookup[id]; +} + +enum smap_id smap_string_to_id(const char *str) { + const struct smap_mapping *m; + + assert(str); + m = smap_mapping_lookup(str, + strlen(str)); + return m ? m->id : SMAPS_ID_INVALID; +} diff --git a/src/libsystem/proc.c b/src/libsystem/proc.c new file mode 100644 index 0000000..49ddfde --- /dev/null +++ b/src/libsystem/proc.c @@ -0,0 +1,416 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <limits.h> + +#include "libsystem.h" +#include "proc.h" + +ssize_t proc_cmdline_get_str(char **buf, const char *op) { + _cleanup_free_ char *cmdline = NULL; + char *s, *w, *state; + size_t l, ll; + int r; + + assert(buf); + assert(op); + + r = read_one_line_from_path("/proc/cmdline", &cmdline); + if (r < 0) + return r; + + ll = strlen(op); + FOREACH_WORD(w, l, cmdline, state) + if (strneq(op, w, ll)) { + s = new0(char, l - ll + 1); + if (!s) + return -ENOMEM; + + strncpy(s, w + ll, l - ll + 1); + + *buf = s; + + return l - ll + 1; + } + + return -ENOENT; +} + +/* In old kernel, this symbol maybe NOT */ +#ifndef TASK_COMM_LEN +#define TASK_COMM_LEN 16 +#endif + +int proc_pid_of(const char *pname) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *de; + int r; + + dir = opendir("/proc"); + if (!dir) + return -errno; + + FOREACH_DIRENT(de, dir, return -errno) { + _cleanup_free_ char *path = NULL; + _cleanup_free_ char *comm = NULL; + + if (de->d_type != DT_DIR) + continue; + + if (!is_number(de->d_name, strlen(de->d_name))) + continue; + + r = asprintf(&path, "/proc/%s/comm", de->d_name); + if (r < 0) + return -ENOMEM; + + r = read_one_line_from_path(path, &comm); + if (r < 0) + continue; + + if (strneq(pname, comm, TASK_COMM_LEN - 1)) + return atoi(de->d_name); + } + + return 0; +} + +static void smap_free(struct smap *map) { + if (!map) + return; + + if (map->mode) + free(map->mode); + + if (map->name) + free(map->name); + + free(map); +} + +void smaps_free(struct smaps *maps) { + int i; + + if (!maps) + return; + + for (i = 0; i < maps->n_map; i++) + smap_free(maps->maps[i]); + + free(maps->maps); + free(maps); +} + +static int add_smap_to_smaps(struct smaps *maps, struct smap *map) { + int i; + + assert(maps); + assert(map); + + maps->n_map++; + + maps->maps = (struct smap **) realloc(maps->maps, sizeof(struct smap *) * maps->n_map); + if (!maps->maps) + return -ENOMEM; + + maps->maps[maps->n_map - 1] = map; + + for (i = 0; i < SMAPS_ID_MAX; i++) + maps->sum[i] += map->value[i]; + + return 0; +} + +int proc_pid_get_smaps(pid_t pid, struct smaps **maps, enum smap_mask mask) { + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + struct smaps *m = NULL; + char buf[LINE_MAX]; + bool get_line = true; + int r; + + assert(maps); + + r = asprintf(&path, "/proc/%d/smaps", pid); + if (r < 0) + return -ENOMEM; + + r = access(path, F_OK); + if (r < 0) + return -errno; + + f = fopen(path, "re"); + if (!f) + return -errno; + + m = new0(struct smaps, 1); + if (!m) + return -ENOMEM; + + for (;;) { + struct smap *map = NULL; + int n; + + if (get_line && !fgets(buf, sizeof(buf), f)) { + if (ferror(f)) { + r = -errno; + goto on_error; + } + break; + } else + get_line = true; + + map = new0(struct smap, 1); + if (!map) { + r = -errno; + goto on_error; + } + + n = sscanf(buf, "%x-%x %ms %*s %*s %*s %ms", &map->start, &map->end, &map->mode, &map->name); + + if (n == 3 && !map->name) + map->name = strdup("[anon]"); + else if (n != 4) { + free(map); + r = -EINVAL; + goto on_error; + } + + for (;;) { + unsigned int v = 0; + enum smap_id id; + size_t l; + + if (!fgets(buf, sizeof(buf), f)) { + if (ferror(f)) { + free(map); + r = -errno; + goto on_error; + } + break; + } + + if ((*buf >= '0' && *buf <= '9') || (*buf >= 'a' && *buf <= 'f')) { + get_line = false; + break; + } + + l = strcspn(buf, ":"); + if (!l) + break; + + buf[l] = 0; + + id = smap_string_to_id(buf); + if (id < 0 || id >= SMAPS_ID_MAX) + continue; + + if (!(mask & (1 << id))) + continue; + + if (sscanf(buf + l + 1, "%d kB", &v) != 1) + break; + + map->value[id] = v; + } + + r = add_smap_to_smaps(m, map); + if (r < 0) + goto on_error; + } + + *maps = m; + + return 0; + +on_error: + smaps_free(m); + return r; +} + +static const char* const meminfo_string_lookup[MEMINFO_ID_MAX] = { + [MEMINFO_ID_MEM_TOTAL] = "MemTotal", + [MEMINFO_ID_MEM_FREE] = "MemFree", + [MEMINFO_ID_MEM_AVAILABLE] = "MemAvailable", + [MEMINFO_ID_BUFFERS] = "Buffers", + [MEMINFO_ID_CACHED] = "Cached", + [MEMINFO_ID_SWAP_CACHED] = "SwapCached", + [MEMINFO_ID_ACTIVE] = "Active", + [MEMINFO_ID_INACTIVE] = "Inactive", + [MEMINFO_ID_ACTIVE_ANON] = "Active(anon)", + [MEMINFO_ID_INACTIVE_ANON] = "Inactive(anon)", + [MEMINFO_ID_ACTIVE_FILE] = "Active(file)", + [MEMINFO_ID_INACTIVE_FILE] = "Inactive(file)", + [MEMINFO_ID_UNEVICTABLE] = "Unevictable", + [MEMINFO_ID_MLOCKED] = "Mlocked", + [MEMINFO_ID_HIGH_TOTAL] = "HighTotal", + [MEMINFO_ID_HIGH_FREE] = "HighFree", + [MEMINFO_ID_LOW_TOTAL] = "LowTotal", + [MEMINFO_ID_LOW_FREE] = "LowFree", + [MEMINFO_ID_SWAP_TOTAL] = "SwapTotal", + [MEMINFO_ID_SWAP_FREE] = "SwapFree", + [MEMINFO_ID_DIRTY] = "Dirty", + [MEMINFO_ID_WRITEBACK] = "Writeback", + [MEMINFO_ID_ANON_PAGES] = "AnonPages", + [MEMINFO_ID_MAPPED] = "Mapped", + [MEMINFO_ID_SHMEM] = "Shmem", + [MEMINFO_ID_SLAB] = "Slab", + [MEMINFO_ID_SRECLAIMABLE] = "SReclaimable", + [MEMINFO_ID_SUNRECLAIM] = "SUnreclaim", + [MEMINFO_ID_KERNEL_STACK] = "KernelStack", + [MEMINFO_ID_PAGE_TABLES] = "PageTables", + [MEMINFO_ID_NFS_UNSTABLE] = "NFS_Unstable", + [MEMINFO_ID_BOUNCE] = "Bounce", + [MEMINFO_ID_WRITEBACK_TMP] = "WritebackTmp", + [MEMINFO_ID_COMMIT_LIMIT] = "CommitLimit", + [MEMINFO_ID_COMMITTED_AS] = "Committed_AS", + [MEMINFO_ID_VMALLOC_TOTAL] = "VmallocTotal", + [MEMINFO_ID_VMALLOC_USED] = "VmallocUsed", + [MEMINFO_ID_VMALLOC_CHUNK] = "VmallocChunk", +}; + +const char *meminfo_id_to_string(enum meminfo_id id) { + assert(id >= 0 && id < MEMINFO_ID_MAX); + + return meminfo_string_lookup[id]; +} + +int proc_get_meminfo(struct meminfo *mi, enum meminfo_mask mask) { + _cleanup_fclose_ FILE *f = NULL; + enum meminfo_mask remain_mask = mask; + char buf[LINE_MAX]; + + assert(mi); + + memset(mi, 0x0, sizeof(struct meminfo)); + + f = fopen("/proc/meminfo", "r"); + if (!f) + return -errno; + + if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE) + remain_mask |= (MEMINFO_MASK_MEM_FREE | MEMINFO_MASK_CACHED); + + while (remain_mask) { + unsigned int v = 0; + enum meminfo_id id; + size_t l; + + if (!fgets(buf, sizeof(buf), f)) { + if (ferror(f)) + return -errno; + break; + } + + l = strcspn(buf, ":"); + if (!l) + break; + + buf[l] = 0; + + id = meminfo_string_to_id(buf); + if (id < 0 || id >= MEMINFO_ID_MAX) + continue; + + if (!(remain_mask & (1ULL << id))) + continue; + + remain_mask &= ~((1ULL << id)); + + if (sscanf(buf + l + 1, "%d", &v) != 1) + break; + + mi->value[id] = v; + } + + if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE) { + mi->value[MEMINFO_ID_MEM_AVAILABLE] = mi->value[MEMINFO_ID_MEM_FREE] + + mi->value[MEMINFO_ID_CACHED]; + } + + return 0; +} + +void proc_buddyinfo_free(struct buddyinfo *bi) { + if (!bi) + return; + + free(bi->zone); + free(bi); +} + +int proc_get_buddyinfo(const char *zone, struct buddyinfo **bi) { + _cleanup_fclose_ FILE *f = NULL; + char buf[LINE_MAX]; + + assert(zone); + assert(bi); + + f = fopen("/proc/buddyinfo", "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_buddyinfo_free_ struct buddyinfo *b = NULL; + int n; + + if (!fgets(buf, sizeof(buf), f)) { + if (ferror(f)) + return -errno; + + break; + } + + b = new0(struct buddyinfo, 1); + if (!b) + return -ENOMEM; + + n = sscanf(buf, "Node %d, zone %m[^ ] %d %d %d %d %d %d %d %d %d %d %d", + &b->node, + &b->zone, + &b->page[PAGE_4K], + &b->page[PAGE_8K], + &b->page[PAGE_16K], + &b->page[PAGE_32K], + &b->page[PAGE_64K], + &b->page[PAGE_128K], + &b->page[PAGE_256K], + &b->page[PAGE_512K], + &b->page[PAGE_1M], + &b->page[PAGE_2M], + &b->page[PAGE_4M]); + if (n != 13) + break; + + if (!streq(zone, b->zone)) + continue; + + *bi = b; + b = NULL; + + return 0; + } + + return -ENODATA; +} diff --git a/src/libsystem/proc.h b/src/libsystem/proc.h new file mode 100644 index 0000000..995cd00 --- /dev/null +++ b/src/libsystem/proc.h @@ -0,0 +1,437 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * libsystem + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file proc.h + * + * procfs utility library + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#pragma once + +#include <sys/types.h> +#include "libsystem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup PROC_GROUP proc group + * + * @brief A set utility library for /proc. Some of library functions + * are only able to be successful root uid with security permissions. + * + * @{ + */ + +/** + * @brief Get string with operator from /proc/cmdline. If foo=bar is + * included in /proc/cmdline and want to get the bar, then: + * \code{.c} + char *buf; + + proc_cmdline_get_str(&buf, "foo="); + * \endcode + * + * @param buf The value string is filled to here. This value has to be + * free-ed by caller. + * @param op An operator string. + * + * @return Result string. This value has to be free-ed by caller. + */ +ssize_t proc_cmdline_get_str(char **buf, const char *op); + +/** + * @brief Get PID of process. + * + * @param pname Process name. + * + * @return PID on successful find. If not found, 0 is returned. And + * -errno is returned on failure. + */ +int proc_pid_of(const char *pname); + +/** + * smaps id + */ +enum smap_id { + SMAPS_ID_INVALID = -1, + SMAPS_ID_ANON_HUGE_PAGES = 0, + SMAPS_ID_ANONYMOUS, + SMAPS_ID_KERNEL_PAGE_SIZE, + SMAPS_ID_LOCKED, + SMAPS_ID_MMU_PAGE_SIZE, + SMAPS_ID_PSWAP, + SMAPS_ID_PRIVATE_CLEAN, + SMAPS_ID_PRIVATE_DIRTY, + SMAPS_ID_PSS, + SMAPS_ID_REFERENCED, + SMAPS_ID_RSS, + SMAPS_ID_SHARED_CLEAN, + SMAPS_ID_SHARED_DIRTY, + SMAPS_ID_SIZE, + SMAPS_ID_SWAP, + SMAPS_ID_MAX, +}; + +/** + * smaps mask + */ +enum smap_mask { + SMAPS_MASK_ANON_HUGE_PAGES = 1 << SMAPS_ID_ANON_HUGE_PAGES, + SMAPS_MASK_ANONYMOUS = 1 << SMAPS_ID_ANONYMOUS, + SMAPS_MASK_KERNEL_PAGE_SIZE = 1 << SMAPS_ID_KERNEL_PAGE_SIZE, + SMAPS_MASK_LOCKED = 1 << SMAPS_ID_LOCKED, + SMAPS_MASK_MMU_PAGE_SIZE = 1 << SMAPS_ID_MMU_PAGE_SIZE, + SMAPS_MASK_PSWAP = 1 << SMAPS_ID_PSWAP, + SMAPS_MASK_PRIVATE_CLEAN = 1 << SMAPS_ID_PRIVATE_CLEAN, + SMAPS_MASK_PRIVATE_DIRTY = 1 << SMAPS_ID_PRIVATE_DIRTY, + SMAPS_MASK_PSS = 1 << SMAPS_ID_PSS, + SMAPS_MASK_REFERENCED = 1 << SMAPS_ID_REFERENCED, + SMAPS_MASK_RSS = 1 << SMAPS_ID_RSS, + SMAPS_MASK_SHARED_CLEAN = 1 << SMAPS_ID_SHARED_CLEAN, + SMAPS_MASK_SHARED_DIRTY = 1 << SMAPS_ID_SHARED_DIRTY, + SMAPS_MASK_SIZE = 1 << SMAPS_ID_SIZE, + SMAPS_MASK_SWAP = 1 << SMAPS_ID_SWAP, + SMAPS_MASK_ALL = (1 << SMAPS_ID_MAX) - 1, + SMAPS_MASK_DEFAULT = (SMAPS_MASK_SIZE | + SMAPS_MASK_RSS | + SMAPS_MASK_PSS | + SMAPS_MASK_SHARED_CLEAN | + SMAPS_MASK_SHARED_DIRTY | + SMAPS_MASK_PRIVATE_CLEAN | + SMAPS_MASK_PRIVATE_DIRTY | + SMAPS_MASK_SWAP | + SMAPS_MASK_PSWAP), +}; + +/** + * a smap info + */ +struct smap { + /** + * start address + */ + unsigned int start; + /** + * end address + */ + unsigned int end; + /** + * smaps mode + */ + char *mode; + /** + * smaps name + */ + char *name; + /** + * value of each + */ + unsigned int value[SMAPS_ID_MAX]; +}; + +/** + * a smaps info of pid + */ +struct smaps { + /** + * sum value of each + */ + unsigned int sum[SMAPS_ID_MAX]; + /** + * number of maps + */ + int n_map; + /** + * maps + */ + struct smap **maps; +}; + +/** + * @brief Destroy struct smaps + * + * @param maps a smaps + */ +void smaps_free(struct smaps *maps); + +static inline void smaps_freep(struct smaps **maps) +{ + if (*maps) + smaps_free(*maps); +} + +/** + * Declare struct smaps with cleanup attribute. Allocated struct smaps + * is destroyed on going out the scope. + */ +#define _cleanup_smaps_free_ _cleanup_ (smaps_freep) + +/** + * @brief Convert smap id to string + * + * @param id smap id + * + * @return converted string + */ +const char *smap_id_to_string(enum smap_id id); + +/** + * @brief Convert smap string to id + * + * @param str smap string + * + * @return converted id + */ +enum smap_id smap_string_to_id(const char *str); + +/** + * @brief Get smaps info of pid + * + * @param pid a pid to get + * @param maps parsed smaps struct. This value has to be destoryed by + * caller. #_cleanup_smaps_free_ is useful to make allocated struct to + * autofree. + * @code{.c} + { + _cleanup_smaps_free_ struct smaps *maps; + + proc_pid_get_smaps(pid, &maps, SMAPS_MASK_ALL); + } + * @endcode + * @param mask mask to parse smaps. + * + * @return 0 on success, -errno on failure. + */ +int proc_pid_get_smaps(pid_t pid, struct smaps **maps, enum smap_mask mask); + +/** + * meminfo id + */ +enum meminfo_id { + MEMINFO_ID_INVALID = -1, + MEMINFO_ID_MEM_TOTAL = 0, + MEMINFO_ID_MEM_FREE, + MEMINFO_ID_MEM_AVAILABLE, + MEMINFO_ID_BUFFERS, + MEMINFO_ID_CACHED, + MEMINFO_ID_SWAP_CACHED, + MEMINFO_ID_ACTIVE, + MEMINFO_ID_INACTIVE, + MEMINFO_ID_ACTIVE_ANON, + MEMINFO_ID_INACTIVE_ANON, + MEMINFO_ID_ACTIVE_FILE, + MEMINFO_ID_INACTIVE_FILE, + MEMINFO_ID_UNEVICTABLE, + MEMINFO_ID_MLOCKED, + MEMINFO_ID_HIGH_TOTAL, + MEMINFO_ID_HIGH_FREE, + MEMINFO_ID_LOW_TOTAL, + MEMINFO_ID_LOW_FREE, + MEMINFO_ID_SWAP_TOTAL, + MEMINFO_ID_SWAP_FREE, + MEMINFO_ID_DIRTY, + MEMINFO_ID_WRITEBACK, + MEMINFO_ID_ANON_PAGES, + MEMINFO_ID_MAPPED, + MEMINFO_ID_SHMEM, + MEMINFO_ID_SLAB, + MEMINFO_ID_SRECLAIMABLE, + MEMINFO_ID_SUNRECLAIM, + MEMINFO_ID_KERNEL_STACK, + MEMINFO_ID_PAGE_TABLES, + MEMINFO_ID_NFS_UNSTABLE, + MEMINFO_ID_BOUNCE, + MEMINFO_ID_WRITEBACK_TMP, + MEMINFO_ID_COMMIT_LIMIT, + MEMINFO_ID_COMMITTED_AS, + MEMINFO_ID_VMALLOC_TOTAL, + MEMINFO_ID_VMALLOC_USED, + MEMINFO_ID_VMALLOC_CHUNK, + MEMINFO_ID_MAX, +}; + +/** + * meminfo mask + */ +enum meminfo_mask { + MEMINFO_MASK_MEM_TOTAL = 1ULL << MEMINFO_ID_MEM_TOTAL, + MEMINFO_MASK_MEM_FREE = 1ULL << MEMINFO_ID_MEM_FREE, + MEMINFO_MASK_MEM_AVAILABLE = 1ULL << MEMINFO_ID_MEM_AVAILABLE, + MEMINFO_MASK_BUFFERS = 1ULL << MEMINFO_ID_BUFFERS, + MEMINFO_MASK_CACHED = 1ULL << MEMINFO_ID_CACHED, + MEMINFO_MASK_SWAP_CACHED = 1ULL << MEMINFO_ID_SWAP_CACHED, + MEMINFO_MASK_ACTIVE = 1ULL << MEMINFO_ID_ACTIVE, + MEMINFO_MASK_INACTIVE = 1ULL << MEMINFO_ID_INACTIVE, + MEMINFO_MASK_ACTIVE_ANON = 1ULL << MEMINFO_ID_ACTIVE_ANON, + MEMINFO_MASK_INACTIVE_ANON = 1ULL << MEMINFO_ID_INACTIVE_ANON, + MEMINFO_MASK_ACTIVE_FILE = 1ULL << MEMINFO_ID_ACTIVE_FILE, + MEMINFO_MASK_INACTIVE_FILE = 1ULL << MEMINFO_ID_INACTIVE_FILE, + MEMINFO_MASK_UNEVICTABLE = 1ULL << MEMINFO_ID_UNEVICTABLE, + MEMINFO_MASK_MLOCKED = 1ULL << MEMINFO_ID_MLOCKED, + MEMINFO_MASK_HIGH_TOTAL = 1ULL << MEMINFO_ID_HIGH_TOTAL, + MEMINFO_MASK_HIGH_FREE = 1ULL << MEMINFO_ID_HIGH_FREE, + MEMINFO_MASK_LOW_TOTAL = 1ULL << MEMINFO_ID_LOW_TOTAL, + MEMINFO_MASK_LOW_FREE = 1ULL << MEMINFO_ID_LOW_FREE, + MEMINFO_MASK_SWAP_TOTAL = 1ULL << MEMINFO_ID_SWAP_TOTAL, + MEMINFO_MASK_SWAP_FREE = 1ULL << MEMINFO_ID_SWAP_FREE, + MEMINFO_MASK_DIRTY = 1ULL << MEMINFO_ID_DIRTY, + MEMINFO_MASK_WRITEBACK = 1ULL << MEMINFO_ID_WRITEBACK, + MEMINFO_MASK_ANON_PAGES = 1ULL << MEMINFO_ID_ANON_PAGES, + MEMINFO_MASK_MAPPED = 1ULL << MEMINFO_ID_MAPPED, + MEMINFO_MASK_SHMEM = 1ULL << MEMINFO_ID_SHMEM, + MEMINFO_MASK_SLAB = 1ULL << MEMINFO_ID_SLAB, + MEMINFO_MASK_SRECLAIMABLE = 1ULL << MEMINFO_ID_SRECLAIMABLE, + MEMINFO_MASK_SUNRECLAIM = 1ULL << MEMINFO_ID_SUNRECLAIM, + MEMINFO_MASK_KERNEL_STACK = 1ULL << MEMINFO_ID_KERNEL_STACK, + MEMINFO_MASK_PAGE_TABLES = 1ULL << MEMINFO_ID_PAGE_TABLES, + MEMINFO_MASK_NFS_UNSTABLE = 1ULL << MEMINFO_ID_NFS_UNSTABLE, + MEMINFO_MASK_BOUNCE = 1ULL << MEMINFO_ID_BOUNCE, + MEMINFO_MASK_WRITEBACK_TMP = 1ULL << MEMINFO_ID_WRITEBACK_TMP, + MEMINFO_MASK_COMMIT_LIMIT = 1ULL << MEMINFO_ID_COMMIT_LIMIT, + MEMINFO_MASK_COMMITTED_AS = 1ULL << MEMINFO_ID_COMMITTED_AS, + MEMINFO_MASK_VMALLOC_TOTAL = 1ULL << MEMINFO_ID_VMALLOC_TOTAL, + MEMINFO_MASK_VMALLOC_USED = 1ULL << MEMINFO_ID_VMALLOC_USED, + MEMINFO_MASK_VMALLOC_CHUNK = 1ULL << MEMINFO_ID_VMALLOC_CHUNK, + MEMINFO_MASK_ALL = (1ULL << MEMINFO_ID_MAX) - 1, +}; + +/** + * meminfo + */ +struct meminfo { + unsigned int value[MEMINFO_ID_MAX]; +}; + +/** + * @brief Convert meminfo id to string + * + * @param id meminfo id + * + * @return converted string + */ +const char *meminfo_id_to_string(enum meminfo_id id); + +/** + * @brief Convert meminfo string to id + * + * @param str meminfo string + * + * @return converted id + */ +enum meminfo_id meminfo_string_to_id(const char *str); + +/** + * @brief Get system memory info(/proc/meminfo) + * @code{.c} + unsigned int get_mem_available(void) + { + struct meminfo mi; + + proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE); + } + * @endcode + * + * @param mi parsed meminfo struct. + * @param mask mask to get meminfo. + * + * @return 0 on success, -errno on failure. + */ +int proc_get_meminfo(struct meminfo *mi, enum meminfo_mask mask); + +/** + * /proc/buddyinfo page index + */ +enum { + PAGE_4K = 0, + PAGE_8K, + PAGE_16K, + PAGE_32K, + PAGE_64K, + PAGE_128K, + PAGE_256K, + PAGE_512K, + PAGE_1M, + PAGE_2M, + PAGE_4M, + PAGE_MAX, +}; + +/** + * A zone buddy info + */ +struct buddyinfo { + /** + * Zone name + */ + char *zone; + /** + * Node number + */ + int node; + /** + * Each pages size + */ + int page[PAGE_MAX]; +}; + +/** + * @brief free struct buddyinfo + * + * @param bi a buddyinfo + */ +void proc_buddyinfo_free(struct buddyinfo *bi); + +static inline void buddyinfo_freep(struct buddyinfo **bi) +{ + proc_buddyinfo_free(*bi); +} + +/** + * Declare struct buddyinfo with cleanup attribute. Allocated struct + * buddyinfo is destroyed on going out the scope. + */ +#define _cleanup_buddyinfo_free_ _cleanup_(buddyinfo_freep) + +/** + * @brief Parse a zone in /proc/buddyinfo + * + * @param zone A zone to parse such like "Normal" + * + * @param bi Allocated and parsed buddyinfo for given zone. This value + * has to be destroyed by caller. #_cleanup_buddyinfo_free_ is useful + * to make autofree this value. + * + * @return 0 on success, -errno on failure. + */ +int proc_get_buddyinfo(const char *zone, struct buddyinfo **bi); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif diff --git a/src/libsystem/time-util.c b/src/libsystem/time-util.c new file mode 100644 index 0000000..28ac86f --- /dev/null +++ b/src/libsystem/time-util.c @@ -0,0 +1,87 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * time-util.c + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> + +#include "libsystem.h" + +#define TIME_BUF_LEN 30 + +int sec_to_timestr(time_t sec, const char *format, char **time) { + struct tm tm; + char *buf; + size_t l; + + assert(format); + assert(time); + + localtime_r(&sec, &tm); + + buf = new0(char, TIME_BUF_LEN); + if (!buf) + return -ENOMEM; + + l = strftime(buf, TIME_BUF_LEN, format, &tm); + if (l <= 0) { + free(buf); + return -EINVAL; + } + + *time = buf; + + return 0; +} + +int sec_to_timestr_full(time_t sec, char **time) { + assert(time); + + return sec_to_timestr(sec, DOW_YYYY_MM_DD_HH_MM_SS_Z, time); +} + +int timestr_to_sec(const char *format, const char *time, time_t *sec) { + struct tm tm; + char *ret; + + assert(format); + assert(time); + assert(sec); + + memset(&tm, 0, sizeof(struct tm)); + ret = strptime(time, format, &tm); + if ((ret - time) < strlen(time)) + return -EINVAL; + + *sec = mktime(&tm); + + return 0; +} + +void msec_to_timeval(uint64_t msec, struct timeval *tv) { + assert(tv); + + tv->tv_sec = msec / MSEC_PER_SEC; + tv->tv_usec = (msec % MSEC_PER_SEC) * USEC_PER_MSEC; +} |