diff options
-rwxr-xr-x | autogen.sh | 31 | ||||
-rw-r--r-- | configure.ac | 29 | ||||
-rw-r--r-- | packaging/libsystem.spec | 22 | ||||
-rw-r--r-- | src/Makefile.am | 26 | ||||
-rw-r--r-- | src/libsystem-sd/systemd.c | 34 | ||||
-rw-r--r-- | src/libsystem/.gitignore | 4 | ||||
-rw-r--r-- | src/libsystem/libsystem.c | 91 | ||||
-rw-r--r-- | src/libsystem/libsystem.h | 91 | ||||
-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 | 341 | ||||
-rw-r--r-- | src/libsystem/proc.h | 437 | ||||
-rw-r--r-- | src/test/test-cp.c | 139 |
13 files changed, 1279 insertions, 103 deletions
@@ -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; +} |