summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xautogen.sh31
-rw-r--r--configure.ac29
-rw-r--r--packaging/libsystem.spec22
-rw-r--r--src/Makefile.am26
-rw-r--r--src/libsystem-sd/systemd.c34
-rw-r--r--src/libsystem/.gitignore4
-rw-r--r--src/libsystem/libsystem.c91
-rw-r--r--src/libsystem/libsystem.h91
-rw-r--r--src/libsystem/proc-meminfo-lookup.gperf67
-rw-r--r--src/libsystem/proc-smaps-lookup.gperf70
-rw-r--r--src/libsystem/proc.c341
-rw-r--r--src/libsystem/proc.h437
-rw-r--r--src/test/test-cp.c139
13 files changed, 1279 insertions, 103 deletions
diff --git a/autogen.sh b/autogen.sh
index 7d3fd65..3825cb5 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -2,11 +2,13 @@
set -e
+opt="$1"
+
if [ -f .git/hooks/pre-commit.sample ] && [ ! -f .git/hooks/pre-commit ]; then
# This part is allowed to fail
cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \
- chmod +x .git/hooks/pre-commit && \
- echo "Activated pre-commit hook." || :
+ chmod +x .git/hooks/pre-commit && \
+ echo "Activated pre-commit hook." || :
fi
# README and INSTALL are required by automake, but may be deleted by
@@ -15,4 +17,29 @@ fi
# ./configure anyway.
touch README INSTALL
+# make sure m4 dir exist
+mkdir -p m4
+
autoreconf --force --install --verbose || exit $?
+
+if [ "x$opt" = "xc" ]; then
+ set -x
+ ./configure $args
+ make clean > /dev/null
+elif [ "x$opt" = "xd" ]; then
+ set -x
+ ./configure CFLAGS='-g -O0 -ftrapv' $args
+ make clean > /dev/null
+elif [ "x$opt" = "xg" ]; then
+ set -x
+ ./configure CFLAGS='-g -Og -ftrapv' $args
+ make clean > /dev/null
+elif [ "x$opt" = "xl" ]; then
+ set -x
+ ./configure CC=clang CFLAGS='-g -O0 -ftrapv' $args
+ make clean > /dev/null
+elif [ "x$opt" = "xs" ]; then
+ set -x
+ scan-build ./configure CFLAGS='-std=gnu99 -g -O0 -ftrapv' $args
+ scan-build make
+fi
diff --git a/configure.ac b/configure.ac
index 49bf6cc..df18d8e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,16 +18,19 @@ LT_INIT([disable-static])
# Checks for programs.
AC_PROG_CC
+AC_PROG_CXX
AC_PROG_INSTALL
+AC_PROG_MAKE_SET
# Checks for libraries.
# FIXME: Replace `main' with a function in `-lrt':
AC_CHECK_LIB([rt], [main])
# Checks for header files.
-AC_CHECK_HEADERS([limits.h mntent.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h])
+AC_CHECK_HEADERS([fcntl.h limits.h mntent.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
+AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_INT32_T
AC_TYPE_INT64_T
@@ -44,7 +47,29 @@ AC_FUNC_GETMNTENT
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_MKTIME
AC_FUNC_REALLOC
-AC_CHECK_FUNCS([getmntent gettimeofday localtime_r memset mkdir rmdir strchr strcspn strdup strndup strrchr strspn])
+AC_CHECK_FUNCS([dup2 getmntent gettimeofday localtime_r memset mkdir rmdir strchr strcspn strdup strndup strrchr strspn])
+
+AC_CHECK_TOOL(GPERF, gperf)
+if test -z "$GPERF" ; then
+ AC_MSG_ERROR([*** gperf not found])
+fi
+
+# ------------------------------------------------------------------------------
+our_cflags=" \
+ -g -O2 \
+ -Werror \
+ -fpie"
+
+our_ldflags=" \
+ -Wl,--as-needed \
+ -Wl,--no-undefined \
+ -Wl,--gc-sections \
+ -Wl,-z,relro \
+ -Wl,-z,now \
+ -pie"
+
+AC_SUBST([OUR_CFLAGS], "$our_cflags")
+AC_SUBST([OUR_LDFLAGS], "$our_ldflags")
# ------------------------------------------------------------------------------
PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.3.2])
diff --git a/packaging/libsystem.spec b/packaging/libsystem.spec
index a15de97..a85a0a6 100644
--- a/packaging/libsystem.spec
+++ b/packaging/libsystem.spec
@@ -1,11 +1,13 @@
Name: libsystem
Summary: System Libraries
Version: 4.0
-Release: 2%{?release_flags}
+Release: 3%{?release_flags}
License: Apache-2.0
Group: System/Libraries
Source: %{name}-%{version}.tar.gz
+%if 0%{?_with_tizen}
Source1001: %{name}.manifest
+%endif
BuildRequires: autoconf
BuildRequires: automake
@@ -13,6 +15,7 @@ BuildRequires: libtool
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(glib-2.0) >= 2.44
BuildRequires: pkgconfig(gio-2.0) >= 2.44
+BuildRequires: gperf
Requires: /bin/cp
@@ -54,7 +57,9 @@ Development header files for systemd util.
%setup -q
%build
+%if 0%{?_with_tizen}
cp %{SOURCE1001} .
+%endif
%if 0%{?debug_enable}
export CFLAGS=$(echo $CFLAGS | sed -e 's/^-g / /g' | sed -e 's/ -g / /g')
@@ -63,14 +68,16 @@ export CFLAGS=$(echo $CFLAGS | sed -e 's/-Wp,-D_FORTIFY_SOURCE=2 / /g')
export CFLAGS="-O0 -g $CFLAGS"
%endif
-%autogen
-%reconfigure
+./autogen.sh
+%configure
make %{?_smp_mflags}
%install
%make_install
+rm -f %{buildroot}%{_libdir}/*.la
+
# make sure debugsources.list exist, it used by rpm macro in %file
# section.
touch debugsources.list
@@ -91,27 +98,36 @@ make check
%files
%defattr(-,root,root,-)
+%if 0%{?_with_tizen}
%manifest %{name}.manifest
+%endif
%{_libdir}/libsystem.so.*
%files devel
%defattr(-,root,root,-)
+%if 0%{?_with_tizen}
%manifest %{name}.manifest
+%endif
%{_libdir}/libsystem.so
%{_includedir}/libsystem/config-parser.h
%{_includedir}/libsystem/dbus-util.h
%{_includedir}/libsystem/glib-util.h
%{_includedir}/libsystem/libsystem.h
+%{_includedir}/libsystem/proc.h
%{_libdir}/pkgconfig/libsystem.pc
%files -n libsystem-sd
%defattr(-,root,root,-)
+%if 0%{?_with_tizen}
%manifest %{name}.manifest
+%endif
%{_libdir}/libsystem-sd.so.*
%files -n libsystem-sd-devel
%defattr(-,root,root,-)
+%if 0%{?_with_tizen}
%manifest %{name}.manifest
+%endif
%{_libdir}/libsystem-sd.so
%{_includedir}/libsystem-sd/systemd.h
%{_libdir}/pkgconfig/libsystem-sd.pc
diff --git a/src/Makefile.am b/src/Makefile.am
index e7df956..48ad051 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -50,7 +50,8 @@ libsystem_pkginclude_HEADERS += \
libsystem/config-parser.h \
libsystem/dbus-util.h \
libsystem/glib-util.h \
- libsystem/libsystem.h
+ libsystem/libsystem.h \
+ libsystem/proc.h
lib_LTLIBRARIES += \
libsystem.la
@@ -65,8 +66,18 @@ libsystem_la_SOURCES = \
libsystem/libsystem.c \
libsystem/libsystem.h \
libsystem/proc.c \
+ libsystem/proc-meminfo-lookup.c \
+ libsystem/proc-smaps-lookup.c \
libsystem/time-util.c
+EXTRA_DIST += \
+ libsystem/proc-meminfo-lookup.gperf \
+ libsystem/proc-smaps-lookup.gperf
+
+CLEANFILES += \
+ libsystem/proc-meminfo-lookup.c \
+ libsystem/proc-smaps-lookup.c
+
libsystem_la_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
@@ -93,6 +104,15 @@ test_exec_LDADD = \
tests += test-exec
# ------------------------------------------------------------------------------
+test_cp_SOURCES = \
+ test/test-cp.c
+
+test_cp_LDADD = \
+ libsystem.la
+
+tests += test-cp
+
+# ------------------------------------------------------------------------------
pkgconfiglib_DATA += \
libsystem-sd/libsystem-sd.pc
@@ -149,4 +169,8 @@ SED_PROCESS = \
%.pc: %.pc.in
$(SED_PROCESS)
+%.c: %.gperf
+ $(AM_V_at)$(MKDIR_P) $(dir $@)
+ $(AM_V_GPERF)$(GPERF) < $< > $@
+
install-exec-hook: $(INSTALL_EXEC_HOOKS)
diff --git a/src/libsystem-sd/systemd.c b/src/libsystem-sd/systemd.c
index cfcd5d6..ab0439b 100644
--- a/src/libsystem-sd/systemd.c
+++ b/src/libsystem-sd/systemd.c
@@ -87,7 +87,7 @@ static int systemd_call_sync(GDBusConnection *connection,
NULL,
&err);
else {
- g_autofree GDBusProxy *proxy = NULL;
+ g_autoptr(GDBusProxy) proxy = NULL;
proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
@@ -125,8 +125,8 @@ static int systemd_call_sync(GDBusConnection *connection,
}
int systemd_subscribe(GDBusConnection *connection, char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
int r;
r = systemd_call_sync(connection,
@@ -148,8 +148,8 @@ int systemd_subscribe(GDBusConnection *connection, char **err_msg) {
}
int systemd_unsubscribe(GDBusConnection *connection, char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
int r;
r = systemd_call_sync(connection,
@@ -175,8 +175,8 @@ int systemd_get_unit(GDBusConnection *connection,
char **unit,
char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
char *obj = NULL;
int r;
@@ -219,8 +219,8 @@ int systemd_control_unit(GDBusConnection *connection,
char **job,
char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
char *obj = NULL;
int r;
@@ -347,7 +347,7 @@ static int systemd_get_property(GDBusConnection *connection,
GVariant **variant,
char **err_msg) {
- g_autofree GError *error = NULL;
+ g_autoptr(GError) error = NULL;
int r;
assert(name);
@@ -471,7 +471,7 @@ static int systemd_get_service_property(GDBusConnection *connection,
value* result, \
char** err_msg) { \
\
- g_autofree GVariant *var = NULL, *inner = NULL; \
+ g_autoptr(GVariant) var = NULL, inner = NULL; \
int r; \
\
assert(target); \
@@ -606,7 +606,7 @@ void systemd_unit_status_list_free_full(GList *status_list) {
static int systemd_parse_list_units_result(GVariant *result, GList **unit_list) {
char *name, *description, *load_state, *active_state, *sub_state;
char *followed, *obj_path, *job_type, *job_obj_path;
- g_autofree GVariantIter *iter;
+ g_autoptr(GVariantIter) iter;
unsigned int job_id;
GList *list = NULL;
int r;
@@ -728,8 +728,8 @@ on_error:
}
int systemd_get_units_list(GDBusConnection *conn, GList **unit_list, char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
int r;
assert(unit_list);
@@ -786,7 +786,7 @@ void systemd_unit_file_status_list_free_full(GList *status_list) {
}
static int systemd_parse_list_unit_files_result(GVariant *result, GList **unit_files_list) {
- g_autofree GVariantIter *iter;
+ g_autoptr(GVariantIter) iter;
GList *list = NULL;
char *name, *status;
int r;
@@ -843,8 +843,8 @@ on_error:
}
int systemd_get_unit_files_list(GDBusConnection *conn, GList **unit_files_list, char **err_msg) {
- g_autofree GVariant *reply = NULL;
- g_autofree GError *error = NULL;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GError) error = NULL;
int r;
assert(conn);
diff --git a/src/libsystem/.gitignore b/src/libsystem/.gitignore
index c97363e..154d18c 100644
--- a/src/libsystem/.gitignore
+++ b/src/libsystem/.gitignore
@@ -1 +1,3 @@
-/libsystem.pc \ No newline at end of file
+/libsystem.pc
+/proc-meminfo-lookup.c
+/proc-smaps-lookup.c \ No newline at end of file
diff --git a/src/libsystem/libsystem.c b/src/libsystem/libsystem.c
index 0159c03..2a8aced 100644
--- a/src/libsystem/libsystem.c
+++ b/src/libsystem/libsystem.c
@@ -26,6 +26,7 @@
#include <ctype.h>
#include <sys/wait.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <mntent.h>
#include "libsystem.h"
@@ -406,44 +407,90 @@ bool is_number(const char *s, int l) {
return true;
}
-int do_copy(const char *src, const char *dst, const char *option, int64_t timeout_msec) {
- /* TODO
- * change direct execution of cp to c api
- */
- char *argv[] = {"/bin/cp", NULL, NULL, NULL, NULL};
+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);
- assert(option);
- argv[1] = (char *)src;
- argv[2] = (char *)dst;
- argv[3] = (char *)option;
+ 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_fork_exec(argv, NULL, timeout_msec);
+ 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 p;
- int r;
+ int r, p;
assert(path);
l = strlen(path);
- for (p = 0, s = 0; p < l; p += s+1) {
- _cleanup_free_ char *d = NULL;
-
- s = strcspn(path+p, "/");
+ for (p = 0, s = 0; p < l; p += s + 1) {
+ s = strcspn(path + p, "/");
if (!s)
continue;
- d = new0(char, p+s+1);
- if (!d)
- return -ENOMEM;
+ assert(PATH_MAX > p + s + 1);
- r = snprintf(d, p+s+1, "%s", path);
+ r = snprintf(d, p + s + 1, "%s", path);
if (r < 0)
return r;
@@ -731,14 +778,14 @@ int str_to_strv(const char *str, char ***strv, const char *separator) {
FOREACH_WORD_SEPARATOR(w, l, str, separator, state) {
p = strndup(w, l);
if (!p) {
- if (v)
- free(v);
+ free(v);
return -ENOMEM;
}
new = (char **)realloc(v, sizeof(char *) * (i + 2));
if (!new) {
free(p);
+ free(v);
p = NULL;
return -ENOMEM;
}
diff --git a/src/libsystem/libsystem.h b/src/libsystem/libsystem.h
index 096b849..5f6df97 100644
--- a/src/libsystem/libsystem.h
+++ b/src/libsystem/libsystem.h
@@ -260,18 +260,52 @@ char *path_kill_slashes(char *path);
bool is_number(const char *s, int l);
/**
- * @brief Run cp with given src, dst with option. Internally, directly
- * calls /bin/cp with given arguments.
- * @todo change direct calls of /bin/cp to c api.
+ * @brief copy a file with mode, if destination file exists, return
+ * error.
*
- * @param src source
- * @param dst destination
- * @param option cp option
- * @param timeout_msec timeout milliseconds
+ * @param src source file path
+ * @param dst destination file path
+ * @param mode destination file mode
*
- * @return return exit code of /bin/cp or negative errno.
+ * @return 0 on success, -errno on failure. -EALREADY if destination
+ * file exist.
*/
-int do_copy(const char *src, const char *dst, const char *option, int64_t timeout_msec);
+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,
@@ -735,42 +769,6 @@ bool isdir(const char *path);
int touch(const char *path);
/**
- * @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;
-
- 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 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 pid_of(const char *pname);
-
-/**
* @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.
@@ -797,9 +795,6 @@ if (is_mounted("cgroup", "/sys/fs/cgroup", "cgroup2", NULL))
* @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
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
index acac082..6a3d3df 100644
--- a/src/libsystem/proc.c
+++ b/src/libsystem/proc.c
@@ -22,15 +22,12 @@
#include <string.h>
#include <errno.h>
#include <assert.h>
+#include <limits.h>
#include "libsystem.h"
+#include "proc.h"
-/* In old kernel, this symbol maybe NOT */
-#ifndef TASK_COMM_LEN
-#define TASK_COMM_LEN 16
-#endif
-
-ssize_t cmdline_get_str(char **buf, const char *op) {
+ssize_t proc_cmdline_get_str(char **buf, const char *op) {
_cleanup_free_ char *cmdline = NULL;
char *s, *w, *state;
size_t l, ll;
@@ -60,7 +57,12 @@ ssize_t cmdline_get_str(char **buf, const char *op) {
return -ENOENT;
}
-int pid_of(const char *pname) {
+/* 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;
@@ -93,3 +95,328 @@ int pid_of(const char *pname) {
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/test/test-cp.c b/src/test/test-cp.c
new file mode 100644
index 0000000..81253bb
--- /dev/null
+++ b/src/test/test-cp.c
@@ -0,0 +1,139 @@
+/*-*- 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+
+#include "libsystem/libsystem.h"
+
+#define TEST_SRC_FILE "/tmp/test-cp-src"
+#define TEST_DST_FILE "/tmp/test-cp-dst"
+
+static int random_char(char **buf, size_t len) {
+ static int rand_init = 0;
+ char *b;
+ int rnd;
+ size_t s;
+
+ if (!rand_init) {
+ srand(time(NULL));
+ rand_init = 1;
+ }
+
+ b = new0(char, len);
+ if (!b)
+ return -ENOMEM;
+
+ for (s = 0; s < len; s++) {
+
+ /* 98 = number_of_chars((sp) ~ (DEL) + \t + \n + \r) */
+ rnd = rand() % 98;
+
+ assert(rnd < 98);
+
+ switch (rnd) {
+ case 95:
+ b[s] = '\t';
+ break;
+ case 96:
+ b[s] = '\n';
+ break;
+ case 97:
+ b[s] = '\r';
+ break;
+ default:
+ b[s] = ' ' + rnd;
+ break;
+ }
+ }
+
+ *buf = b;
+
+ return 0;
+}
+
+static int write_src_file(unsigned int n_byte) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(random_char(&buf, n_byte) == 0);
+
+ fd = open(TEST_SRC_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ assert(fd >= 0);
+ assert(write(fd, buf, n_byte) == n_byte);
+
+ return 0;
+}
+
+static void compare_file(void) {
+ _cleanup_close_ int src_fd = -1, dst_fd = -1;
+ char src_buf[1024], dst_buf[1024];
+ ssize_t src_red, dst_red;
+
+ src_fd = open(TEST_SRC_FILE, O_RDONLY);
+ assert(src_fd >= 0);
+
+ dst_fd = open(TEST_DST_FILE, O_RDONLY);
+ assert(dst_fd >= 0);
+
+ while(src_red = read(src_fd, src_buf, 1024) > 0,
+ dst_red = read(dst_fd, dst_buf, 1024) > 0) {
+ assert(src_red == dst_red);
+ assert(memcmp(src_buf, dst_buf, src_red) == 0);
+ }
+}
+
+static void test_overwite(void) {
+ assert(unlink(TEST_SRC_FILE) == 0 || errno == ENOENT);
+ assert(unlink(TEST_DST_FILE) == 0 || errno == ENOENT);
+
+ assert(touch(TEST_SRC_FILE) == 0);
+ assert(touch(TEST_DST_FILE) == 0);
+
+ assert(do_copy(TEST_SRC_FILE, TEST_DST_FILE) == -EALREADY);
+ assert(do_copy_force(TEST_SRC_FILE, TEST_DST_FILE) == 0);
+ compare_file();
+}
+
+static void test_n_byte_cp_force(unsigned int n) {
+ assert(write_src_file(n) == 0);
+ assert(do_copy_force(TEST_SRC_FILE, TEST_DST_FILE) == 0);
+ compare_file();
+}
+
+int main(int argc, char *argv[]) {
+ unsigned int b;
+
+ test_overwite();
+
+ for (b = 8; b < (1 << 30); b = b << 1)
+ test_n_byte_cp_force(b);
+
+ unlink(TEST_SRC_FILE);
+ unlink(TEST_DST_FILE);
+
+ return 0;
+}