massive refactoring: clean exit, error handling, etc.
authorAlexey Chernobaev <achernobaev@dev.rtsoft.ru>
Wed, 29 Aug 2018 20:37:44 +0000 (23:37 +0300)
committerAleksei Vereshchagin <avereschagin@dev.rtsoft.ru>
Mon, 3 Sep 2018 15:21:42 +0000 (18:21 +0300)
CMakeLists.txt
logging.c [new file with mode: 0644]
logging.h [new file with mode: 0644]
proc_stat.c [new file with mode: 0644]
proc_stat.h [new file with mode: 0644]
profctl.c
tizen_util.c [new file with mode: 0644]
tizen_util.h [new file with mode: 0644]

index bb5ef9d8306bea0b78537c720437a61361b2a6e4..dea249f3765a28db04c1d4f41f0c33119e9323aa 100644 (file)
@@ -22,6 +22,12 @@ ADD_DEFINITIONS(-DTIZEN=$<BOOL:${TIZEN}>)
 SET(PROFCTL "profctl")
 SET(${PROFCTL}_SOURCE_FILES
     profctl.c
+    logging.c
+    logging.h
+    proc_stat.c
+    proc_stat.h
+    tizen_util.h
+    tizen_util.c
 )
 ADD_EXECUTABLE(${PROFCTL} ${${PROFCTL}_SOURCE_FILES})
 SET_TARGET_PROPERTIES(${PROFCTL} PROPERTIES COMPILE_FLAGS "-fPIE")
@@ -34,4 +40,4 @@ SET_TARGET_PROPERTIES(${PROFCTL}
     PROPERTIES SKIP_BUILD_RPATH TRUE
 ) # remove rpath option that is automatically generated by cmake.
 
-INSTALL(TARGETS ${PROFCTL} DESTINATION ${BINDIR})
+INSTALL(TARGETS ${PROFCTL} DESTINATION ${BINDIR} RUNTIME DESTINATION ${BINDIR})
diff --git a/logging.c b/logging.c
new file mode 100644 (file)
index 0000000..10beacc
--- /dev/null
+++ b/logging.c
@@ -0,0 +1,173 @@
+#include <errno.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "logging.h"
+
+static FILE *log_file = NULL;
+
+static pthread_mutex_t log_lock;
+
+int log_init()
+{
+       if (pthread_mutex_init(&log_lock, NULL) != 0) {
+               perror("mutex init failed\n");
+               return -1;
+       }
+       log_file = stderr;
+       return 0;
+}
+
+void log_uninit()
+{
+       if (log_file != NULL && log_file != stderr) {
+               fclose(log_file);
+               log_file = NULL;
+       }
+       pthread_mutex_destroy(&log_lock);
+}
+
+void log_set_file(FILE *file)
+{
+       log_file = file;
+}
+
+void log_time_zone_info(time_t start_time)
+{
+       struct tm tm = *localtime(&start_time);
+       fprintf(log_file, "Local time: %04d-%02d-%02d %02d:%02d:%02d. Time zone: %s (UTC",
+               tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone);
+       char sign;
+       if (tm.tm_gmtoff >= 0) {
+               sign = '+';
+       }
+       else {
+               sign = '-';
+               tm.tm_gmtoff = -tm.tm_gmtoff;
+       }
+       int minutes = tm.tm_gmtoff / 60;
+       fprintf(log_file, "%c%02d%02d). ", sign, minutes / 60, minutes % 60);
+       fprintf(log_file, "Using UTC time in the log\n");
+}
+
+// et: epoch time; tm: UTC time (broken-down representation); millisec: milliseconds
+int get_current_time(time_t *et, struct tm *tm, int *millisec)
+{
+       struct timeval timeval;
+       int result = gettimeofday(&timeval, NULL);
+       time_t t;
+       if (result == 0) {
+               t = timeval.tv_sec;
+       }
+       else {
+               t = time(NULL);
+               timeval.tv_usec = 0;
+       }
+       int msec = (timeval.tv_usec + 500) / 1000;
+       if (msec >= 1000) {
+               ++t;
+               msec = 0;
+       }
+       if (et != NULL) {
+               *et = t;
+       }
+       if (tm != NULL) {
+               *tm = *gmtime(&t);
+       }
+       *millisec = msec;
+       return result;
+}
+
+static void log_current_time()
+{
+       struct tm tm;
+       int millisec;
+       get_current_time(NULL, &tm, &millisec);
+       fprintf(log_file, "%02d-%02d-%02d %02d:%02d:%02d.%03d ",
+               tm.tm_year + 1900 - 2000, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, millisec);
+}
+
+void log_message(const char *fmt, ...)
+{
+       pthread_mutex_lock(&log_lock);
+       log_current_time();
+       va_list arg;
+       va_start(arg, fmt);
+       vfprintf(log_file, fmt, arg);
+       va_end(arg);
+       fprintf(log_file, "\n");
+       fflush(log_file);
+       pthread_mutex_unlock(&log_lock);
+}
+
+void log_error(const char *fmt, ...)
+{
+       pthread_mutex_lock(&log_lock);
+       fputs("[ERROR] ", log_file);
+       log_current_time();
+       va_list arg;
+       va_start(arg, fmt);
+       vfprintf(log_file, fmt, arg);
+       va_end(arg);
+       fprintf(log_file, "\n");
+       fflush(log_file);
+       pthread_mutex_unlock(&log_lock);
+}
+
+// write an error message to log: first write 'prefix' (if not null), then the current time,
+// then the formatted message, then the system error message (corresponding to 'error_code',
+// and a new line
+static void log_prefixed_system_error(FILE *file, int error_code, const char *prefix, const char *fmt, va_list arg)
+{
+       pthread_mutex_lock(&log_lock);
+       if (file == NULL) {
+               file = log_file;
+       }
+       if (prefix != NULL) {
+               fputs(prefix, file);
+       }
+       log_current_time();
+       vfprintf(file, fmt, arg);
+       fprintf(file, ": %s\n", strerror(error_code));
+       fflush(file);
+       pthread_mutex_unlock(&log_lock);
+}
+
+void log_system_error(const char *fmt, ...)
+{
+       va_list arg;
+       va_start(arg, fmt);
+       log_prefixed_system_error(log_file, errno, NULL, fmt, arg);
+       va_end(arg);
+}
+
+void log_system_error_and_exit(const char *fmt, ...)
+{
+       int error_code = errno;
+       va_list arg;
+       va_start(arg, fmt);
+       log_prefixed_system_error(log_file, error_code, "[FATAL] ", fmt, arg);
+       // repeat to actual stderr
+       if (log_file != stderr) {
+               log_prefixed_system_error(stderr, error_code, "[FATAL] ", fmt, arg);
+       }
+       va_end(arg);
+       exit(1);
+}
+/*
+void log_system_error_and_thread_exit(const char *fmt, ...)
+{
+       int error_code = errno;
+       va_list arg;
+       va_start(arg, fmt);
+       log_prefixed_system_error(log_file, error_code, "[THREAD EXIT] ", fmt, arg);
+       // repeat to actual stderr
+       if (log_file != stderr) {
+               log_prefixed_system_error(stderr, error_code, "[THREAD EXIT] ", fmt, arg);
+       }
+       va_end(arg);
+       pthread_exit(NULL);
+}
+*/
diff --git a/logging.h b/logging.h
new file mode 100644 (file)
index 0000000..80ce126
--- /dev/null
+++ b/logging.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <stdio.h>
+#include <sys/time.h>
+
+int log_init();
+void log_uninit();
+void log_set_file(FILE *file);
+int get_current_time(time_t *et, struct tm *tm, int *millisec);
+void log_time_zone_info(time_t start_time);
+void log_message(const char *fmt, ...);
+void log_error(const char *fmt, ...);
+void log_system_error(const char *fmt, ...);
+void log_system_error_and_exit(const char *fmt, ...);
+void log_system_error_and_thread_exit(const char *fmt, ...);
diff --git a/proc_stat.c b/proc_stat.c
new file mode 100644 (file)
index 0000000..bf98d8e
--- /dev/null
@@ -0,0 +1,312 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/statvfs.h>
+#include <sys/stat.h>
+
+#include "proc_stat.h"
+#include "logging.h"
+
+// TODO!! check errors?
+static void split(char *line, char **array, int n)
+{
+       int i;
+       for (i = 0; i < n; i++) {
+               while(line[0] == ' ') *line++ = 0;
+               array[i] = line;
+               line = index(line, ' ');
+               if (line == NULL)
+                       break;
+               *line++ = 0;
+       }
+       i++;
+       if (i < n) {
+               int k;
+
+               for (k = 0; k <= i; k++) {
+                       printf("%d == %s\n", k, array[k]);
+               }
+       }
+       for (; i < n; i++) {
+               array[i] = NULL;
+       }
+}
+
+static int is_running(int pid)
+{
+       return kill(pid, 0) == 0;
+}
+
+struct write_proc_stat_context {
+       FILE *sstat;
+       FILE *cpuf;
+       FILE *memf;
+       FILE *pmstat;
+       FILE *pstat;
+};
+
+static void init_write_proc_stat_context(struct write_proc_stat_context *c)
+{
+       memset(c, 0, sizeof(struct write_proc_stat_context));
+}
+
+static void uninit_write_proc_stat_context(struct write_proc_stat_context *c)
+{
+       if (c->sstat) fclose(c->sstat);
+       if (c->cpuf) fclose(c->cpuf);
+       if (c->memf) fclose(c->memf);
+       if (c->pmstat) fclose(c->pmstat);
+       if (c->pstat) fclose(c->pstat);
+}
+
+extern volatile int global_stop;
+
+static void write_proc_stat(FILE *stat_file_out, int pid, int timeout_usec, int verbose)
+{
+       struct write_proc_stat_context c;
+       init_write_proc_stat_context(&c);
+
+       c.sstat = fopen("/proc/stat","r");
+       if (c.sstat == NULL ) {
+               log_system_error("open /proc/stat");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       c.cpuf = fopen("/sys/devices/system/cpu/present","r");
+       if (c.cpuf == NULL ) {
+               log_system_error("open /sys/devices/system/cpu/present");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+       int tmp, ncpu;
+       if (fscanf(c.cpuf, "%d-%d\n", &tmp, &ncpu) < 2) {
+               log_system_error("fscanf(cpuf) failed");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+       fclose(c.cpuf);
+       c.cpuf = NULL;
+       ncpu = ncpu - tmp + 1;
+
+       long psize = sysconf(_SC_PAGESIZE);
+       if (fprintf(stat_file_out, "psize %ld ncpu %d\n", psize, ncpu) < 0) {
+               log_system_error("cannot write stat");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       c.memf = fopen("/proc/meminfo", "r");
+       if (c.memf == NULL ) {
+               log_system_error("open /proc/meminfo");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       char *statmname;
+       if (asprintf(&statmname, "/proc/%d/statm", pid) == -1) {
+               log_system_error("asprintf");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       // Sometimes we can't successfully open /proc/$pid files due to strange
+       // permission problems, so we retry to open it several times.
+
+       int retry = 0;
+       const int max_retry = 10;
+       for (; retry < max_retry; ++retry)
+       {
+               c.pmstat = fopen(statmname, "r");
+               if (c.pmstat == NULL) {
+                       if (!is_running(pid)) { // expected if not running
+                               free(statmname);
+                               uninit_write_proc_stat_context(&c);
+                               return;
+                       }
+                       log_system_error("open %s", statmname);
+                       usleep(100000);
+                       continue;
+               }
+               break;
+       }
+
+       if (c.pmstat == NULL)
+       {
+               if (is_running(pid)) { // expected if not running
+                       log_error("cannot open %s - exiting", statmname);
+               }
+               free(statmname);
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       free(statmname);
+
+       char *statname;
+       if (asprintf(&statname, "/proc/%d/stat", pid) == -1) {
+               log_system_error("asprintf");
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       for (; retry < max_retry; ++retry)
+       {
+               c.pstat = fopen(statname, "r");
+               if (c.pstat == NULL) {
+                       if (!is_running(pid)) {
+                               free(statname);
+                               uninit_write_proc_stat_context(&c);
+                               return;
+                       }
+                       log_system_error("open %s", statname);
+                       usleep(100000);
+                       continue;
+               }
+               break;
+       }
+
+       if (c.pstat == NULL) {
+               if (is_running(pid)) {
+                       log_error("cannot open %s - exiting", statname);
+               }
+               free(statname);
+               uninit_write_proc_stat_context(&c);
+               return;
+       }
+
+       free(statname);
+
+       if (verbose) {
+               log_message("stat output started");
+       }
+
+       const int line_buf_size = 256;
+       ssize_t slen, plen, mtlen, mflen;
+       char *sline = malloc(slen = line_buf_size);
+       char *pline = malloc(plen = line_buf_size);
+       char *mtline = malloc(mtlen = line_buf_size);
+       char *mfline = malloc(mflen = line_buf_size);
+       while(!global_stop) {
+               char *stats[5];
+               char *pstats[18];
+               char *mt[3];
+               char *mf[3];
+               long pages;
+               long available;
+
+               time_t t;
+               int millisec;
+               if (get_current_time(&t, NULL, &millisec) != 0) { // gettimeofday failed (no milliseconds)
+                       timeout_usec = 1000000; // 1 sec
+               }
+
+               fseek(c.sstat, 0, SEEK_SET);
+               fflush(c.sstat);
+               if (getline(&sline, &slen, c.sstat) == -1) {
+                       log_system_error("cannot read sstat");
+                       break;
+               }
+
+               split(sline, stats, sizeof(stats) / sizeof(stats[0]));
+
+               fseek(c.pstat, 0, SEEK_SET);
+               fflush(c.pstat);
+               if (getline(&pline, &plen, c.pstat) == -1) {
+                       if (is_running(pid)) {
+                               log_system_error("cannot read pstat");
+                       }
+                       break;
+               }
+
+               split(pline, pstats, sizeof(pstats) / sizeof(pstats[0]));
+
+               fseek(c.pmstat, 0, SEEK_SET);
+               fflush(c.pmstat);
+               if (fscanf(c.pmstat, "%ld", &pages) < 1) {
+                       log_system_error("fscanf(pmstat) failed");
+                       break;
+               }
+
+               fseek(c.memf, 0, SEEK_SET);
+               fflush(c.memf);
+               if (getline(&mtline, &mtlen, c.memf) == -1 || getline(&mfline, &mflen, c.memf) == -1) {
+                       log_system_error("cannot read memf");
+                       break;
+               }
+
+               split(mtline, mt, 3);
+               split(mfline, mf, 3);
+
+               struct statvfs fstat;
+               if (statvfs(".", &fstat) < 0) {
+                       log_system_error("statvfs");
+                       break;
+               }
+
+               available = (fstat.f_bsize >= 1024)
+                       ? (fstat.f_bsize / 1024) * fstat.f_bavail
+                       : fstat.f_bavail / (1024 / fstat.f_bsize);
+
+               if (fprintf(stat_file_out,
+                       "%ld.%03d" " %s %s %s" " %s %s" " %s %s %ld %ld\n",
+                       t, millisec,
+                       stats[1], stats[3], stats[4], /* user system idle */
+                       pstats[14 - 1], pstats[15 - 1], /* puser psystem */
+                       mt[1], mf[1], available, pages * psize) < 0
+                       ||
+                       fflush(stat_file_out) != 0)
+               {
+                       log_system_error("cannot write stat");
+                       break;
+               }
+
+               usleep(timeout_usec);
+       }
+       free(sline);
+       free(pline);
+       free(mtline);
+       free(mfline);
+       uninit_write_proc_stat_context(&c);
+}
+
+struct outstat_info {
+       FILE *file;
+       int pid;
+       int timeout_usec;
+       int verbose;
+};
+
+static void *outstat_thread(void *arg)
+{
+       struct outstat_info *oi = (struct outstat_info*)arg;
+       write_proc_stat(oi->file, oi->pid, oi->timeout_usec, oi->verbose);
+       free(oi);
+       return NULL;
+}
+
+pthread_t start_write_proc_stat(FILE *output, int pid, int timeout_usec, int verbose)
+{
+       pthread_attr_t attr;
+       if (pthread_attr_init(&attr) != 0) {
+               log_system_error_and_exit("pthread_attr_init");
+       }
+
+       struct outstat_info *oi = malloc(sizeof(struct outstat_info));
+       oi->file = output;
+       oi->pid = pid;
+       oi->timeout_usec = timeout_usec;
+       oi->verbose = verbose;
+
+       pthread_t thread;
+       if (pthread_create(&thread, &attr, outstat_thread, oi) != 0) {
+               log_system_error_and_exit("pthread_create");
+       }
+
+       return thread;
+}
diff --git a/proc_stat.h b/proc_stat.h
new file mode 100644 (file)
index 0000000..17c0b76
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <stdio.h>
+
+pthread_t start_write_proc_stat(FILE *output, int pid, int timeout_usec, int verbose);
index 0abf2fabd37fba057b70579d99bff3d25895ff0e..25aac4a5821614fdb0ed252647b829fc8fdbf040 100644 (file)
--- a/profctl.c
+++ b/profctl.c
 #if TIZEN
 #include <termios.h>
 #include <aul/aul.h>
+#include "tizen_util.h"
 #endif /* TIZEN */
 
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/xattr.h>
-#include <sys/statvfs.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <fcntl.h>
 
-struct app_info {
-       char *appid;
-       int pid;
-};
+#include "logging.h"
+#include "proc_stat.h"
 
 static int verbose = 0;
 static int doinfo = 0;
-static int timeoutMicrosec = 500000; // 0.5 seconds
-static int pid = -1;
+static int stat_period_usec = 500000; // 0.5 seconds
 #if TIZEN
 static struct termios sterm;
 #endif /* TIZEN */
 
 static char *appid = NULL;
+static int app_pid = -1;
 static char *pname = NULL;
 static char *ename = NULL;
 static char *oname = NULL;
 
 static int isPipeOwner = 0;
 
-static FILE *log = NULL;
 static int controlPort = -1;
 static int dataPort = -1;
 static int statPort = -1;
@@ -60,7 +57,7 @@ static FILE *ctrl_file_out = NULL;
 static FILE *data_file_out = NULL;
 static FILE *stat_file_out = NULL;
 
-static volatile int exiting = 0;
+volatile int global_stop = 0;
 
 static struct option long_options[] = {
        {"appid",       required_argument, 0, 'a'},
@@ -78,149 +75,37 @@ static struct option long_options[] = {
        {0, 0, 0, 0}
 };
 
-static void log_time_zone_info(time_t start_time)
-{
-       struct tm tm = *localtime(&start_time);
-       fprintf(log, "Local time: %04d-%02d-%02d %02d:%02d:%02d. Time zone: %s (UTC",
-               tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone);
-       char sign;
-       if (tm.tm_gmtoff >= 0) {
-               sign = '+';
-       }
-       else {
-               sign = '-';
-               tm.tm_gmtoff = -tm.tm_gmtoff;
-       }
-       int minutes = tm.tm_gmtoff / 60;
-       fprintf(log, "%c%02d%02d). ", sign, minutes / 60, minutes % 60);
-       fprintf(log, "Using UTC time in the log\n");
-}
-
-// et: epoch time; tm: UTC time (broken-down representation); millisec: milliseconds
-static int get_current_time(time_t *et, struct tm *tm, int *millisec)
-{
-       struct timeval timeval;
-       int result = gettimeofday(&timeval, NULL);
-       time_t t;
-       if (result == 0) {
-               t = timeval.tv_sec;
-       }
-       else {
-               t = time(NULL);
-               timeval.tv_usec = 0;
-       }
-       int msec = (timeval.tv_usec + 500) / 1000;
-       if (msec >= 1000) {
-               ++t;
-               msec = 0;
-       }
-       if (et != NULL) {
-               *et = t;
-       }
-       if (tm != NULL) {
-               *tm = *gmtime(&t);
-       }
-       *millisec = msec;
-       return result;
-}
-
-static void log_current_time()
-{
-       struct tm tm;
-       int millisec;
-       get_current_time(NULL, &tm, &millisec);
-       fprintf(log, "%02d-%02d-%02d %02d:%02d:%02d.%03d ",
-               tm.tm_year + 1900 - 2000, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, millisec);
-}
-
-static pthread_mutex_t log_lock;
-
-static void flog(const char *fmt, ...)
-{
-       pthread_mutex_lock(&log_lock);
-       log_current_time();
-       va_list arg;
-       va_start(arg, fmt);
-       vfprintf(log, fmt, arg);
-       va_end(arg);
-       fprintf(log, "\n");
-       fflush(log);
-       pthread_mutex_unlock(&log_lock);
-}
-
-// write an error message to log: first write 'prefix' (if not null), then the current time,
-// then the formatted message, then the system error message (corresponding to 'error_code',
-// and a new line
-static void log_prefixed_system_error(FILE *file, int error_code, const char *prefix, const char *fmt, va_list arg)
-{
-       pthread_mutex_lock(&log_lock);
-       if (file == NULL) {
-               file = log;
-       }
-       if (prefix != NULL) {
-               fputs(prefix, file);
-       }
-       log_current_time();
-       vfprintf(file, fmt, arg);
-       fprintf(file, ": %s\n", strerror(error_code));
-       fflush(file);
-       pthread_mutex_unlock(&log_lock);
-}
-
-static void log_system_error(const char *fmt, ...)
-{
-       va_list arg;
-       va_start(arg, fmt);
-       log_prefixed_system_error(log, errno, NULL, fmt, arg);
-       va_end(arg);
-}
-
-static void log_system_error_and_exit(const char *fmt, ...)
-{
-       int error_code = errno;
-       va_list arg;
-       va_start(arg, fmt);
-       log_prefixed_system_error(log, error_code, "[FATAL] ", fmt, arg);
-       // repeat to actual stderr
-       if (log != stderr) {
-               log_prefixed_system_error(stderr, error_code, "[FATAL] ", fmt, arg);
-       }
-       va_end(arg);
-       exit(1);
-}
-
-static void log_system_error_and_thread_exit(const char *fmt, ...)
-{
-       int error_code = errno;
-       va_list arg;
-       va_start(arg, fmt);
-       log_prefixed_system_error(log, error_code, "[THREAD EXIT] ", fmt, arg);
-       // repeat to actual stderr
-       if (log != stderr) {
-               log_prefixed_system_error(stderr, error_code, "[THREAD EXIT] ", fmt, arg);
-       }
-       va_end(arg);
-       pthread_exit(NULL);
-}
+static volatile int data_output_thread_runs = 0;
+static volatile int command_loop_thread_runs = 0;
+static volatile int proc_stat_thread_runs = 0;
+static pthread_t proc_stat_thread_id;
 
 static int openFileProcess();
 static int openPort(int port);
 static FILE *pipef;
 
-static void *output_thread(void *arg)
+static void *data_output_thread(void *arg)
 {
-       if (openFileProcess() == 0) {
+       do
+       {
+               if (openFileProcess() != 0) {
+                       break;
+               }
+
                if (dataPort > 0) {
                        data_socket = openPort(dataPort);
                        if (data_socket < 0) {
-                               exit(1); // TODO!!
+                               break;
                        }
                        data_file_out = fdopen(data_socket, "w");
                        if (data_file_out == NULL) {
-                               log_system_error_and_thread_exit("fdopen(data,w)");
+                               log_system_error("fdopen(data,w)");
+                               break;
+                       }
+                       if (fprintf(data_file_out, "ready\n") < 0 || fflush(data_file_out) != 0) {
+                               log_system_error("cannot write 'ready' prompt to data stream");
+                               break;
                        }
-                       fprintf(data_file_out, "ready\n");
-                       fflush(data_file_out);
                }
                else {
                        data_file_out = stdout;
@@ -230,34 +115,51 @@ static void *output_thread(void *arg)
 
                char *buffer = NULL;
                size_t lsize = 0;
-               while (getline(&buffer, &lsize, pipef) != -1) {
+               while (!global_stop) {
+                       if (getline(&buffer, &lsize, pipef) == -1) {
+                               log_system_error("cannot read data from pipe");
+                               break;
+                       }
                        ++linesRead;
                        if (fputs(buffer, data_file_out) < 0) {
-                               flog("fputs failed - exiting");
+                               log_message("cannot write data");
                                break;
                        }
                }
                free(buffer);
 
                if (verbose) {
-                       flog("output thread exits (%d lines read)", linesRead);
+                       log_message("output thread exits (%d lines read from pipe)", linesRead);
                }
-       }
-       exit(0); // TODO!!
+       } while (0); // one-iteration loop
+       data_output_thread_runs = 0;
+       global_stop = 1;
+       return NULL;
 }
 
-#if TIZEN
-static void *wait_thread(void *arg)
+static void *wait_app_exited_thread(void *arg)
 {
-       while (aul_app_get_status_bypid(pid) >= 0) {
-               sleep(1);
+       intptr_t app_pid = (intptr_t)arg;
+       int app_runs = 1;
+#if TIZEN
+       while (!global_stop && (app_runs = (aul_app_get_status_bypid(app_pid) >= 0))) {
+               usleep(500000);
+       }
+#else
+       while (!global_stop && (app_runs = (kill(app_pid, 0) == 0))) {
+               usleep(500000);
        }
+#endif
        if (verbose) {
-               flog("application finished");
+               if (!app_runs) {
+                       log_message("controlled application finished (pid=%d)", app_pid);
+               }
+               if (!global_stop) {
+                       log_message("stop flag set (wait_app_exited_thread)");
+               }
        }
-       exiting = 1; // TODO!! exit?
+       global_stop = 1;
 }
-#endif /* TIZEN */
 
 // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed
 // TODO!! is needed?
@@ -270,7 +172,7 @@ static void close_stdio()
 
 static void finish()
 {
-       flog("exit handler called");
+       log_message("exit handler called");
 #if TIZEN
        if (controlPort < 0) {
                tcsetattr(0, TCSANOW, &sterm);
@@ -303,231 +205,20 @@ static void finish()
        if (stat_socket >= 0) {
                close(stat_socket);
        }
-       flog("=== finished ===");
-       if (log != stderr) {
-               fclose(log);
-       }
+       log_message("=== finished ===");
+       log_uninit();
        kill(getpid(), SIGKILL);
 }
 
-static void split(char *line, char **array, int n)
-{
-       int i;
-       for (i = 0; i < n; i++) {
-               while(line[0] == ' ') *line++ = 0;
-               array[i] = line;
-               line = index(line, ' ');
-               if (line == NULL)
-                       break;
-               *line++ = 0;
-       }
-       i++;
-       if (i < n) {
-               int k;
-
-               for (k = 0; k <= i; k++) {
-                       printf("%d == %s\n", k, array[k]);
-               }
-       }
-       for (; i < n; i++) {
-               array[i] = NULL;
-       }
-}
-
-static void *outstat_thread(void *arg)
-{
-       FILE *sstat = fopen("/proc/stat","r");
-       if (sstat == NULL ) {
-               log_system_error_and_thread_exit("open /proc/stat");
-       }
-
-       FILE *cpuf = fopen("/sys/devices/system/cpu/present","r");
-       if (cpuf == NULL ) {
-               log_system_error_and_thread_exit("open /sys/devices/system/cpu/present");
-       }
-       int tmp, ncpu;
-       if (fscanf(cpuf, "%d-%d\n", &tmp, &ncpu) < 2) {
-               log_system_error_and_thread_exit("fscanf(cpuf) failed");
-       }
-       fclose(cpuf);
-       ncpu = ncpu - tmp + 1;
-
-       if (stat_file_out == NULL) {
-               stat_file_out = stdout;
-       }
-
-       long psize = sysconf(_SC_PAGESIZE);
-       fprintf(stat_file_out, "psize %ld ncpu %d\n", psize, ncpu);
-
-       FILE *memf = fopen("/proc/meminfo", "r");
-       if (memf == NULL ) {
-               log_system_error_and_thread_exit("open /proc/meminfo");
-       }
-
-       char *statmname;
-       if (asprintf(&statmname, "/proc/%d/statm", pid) == -1) {
-               log_system_error_and_thread_exit("asprintf");
-       }
-
-       char *statname;
-       if (asprintf(&statname, "/proc/%d/stat", pid) == -1) {
-               log_system_error_and_thread_exit("asprintf");
-       }
-
-       // Sometimes we can't successfully open /proc/$pid files due to strange
-       // permission problems, so we retry to open it several times.
-
-       FILE *pmstat;
-
-       int retry = 0;
-       const int max_retry = 10;
-       for (; retry < max_retry; ++retry)
-       {
-               pmstat = fopen(statmname, "r");
-               if (pmstat == NULL) {
-                       log_system_error("open %s", statmname);
-                       usleep(100000);
-                       continue;
-               }
-               break;
-       }
-
-       if (pmstat == NULL)
-       {
-               flog("cannot open %s - exiting", statmname);
-               pthread_exit(NULL);
-       }
-
-       FILE *pstat;
-       for (; retry < max_retry; ++retry)
-       {
-               pstat = fopen(statname, "r");
-               if (pstat == NULL) {
-                       log_system_error("open %s", statname);
-                       usleep(100000);
-                       continue;
-               }
-               break;
-       }
-
-       if (pstat == NULL) {
-               flog("cannot open %s - exiting", statname);
-               pthread_exit(NULL);
-       }
-
-       free(statmname);
-       free(statname);
-
-       if (verbose) {
-               flog("stat output started");
-       }
-
-       // TODO!! free buffers correctly
-       const int line_buf_size = 256;
-       ssize_t slen, plen, mtlen, mflen;
-       char *sline = malloc(slen = line_buf_size);
-       char *pline = malloc(plen = line_buf_size);
-       char *mtline = malloc(mtlen = line_buf_size);
-       char *mfline = malloc(mflen = line_buf_size);
-       while(1) {
-               char *stats[5];
-               char *pstats[18];
-               char *mt[3];
-               char *mf[3];
-               long pages;
-               long available;
-
-               time_t t;
-               int millisec;
-               if (get_current_time(&t, NULL, &millisec) != 0) { // gettimeofday failed (no milliseconds)
-                       timeoutMicrosec = 1000000; // 1 sec
-               }
-
-               fseek(sstat, 0, SEEK_SET);
-               fflush(sstat);
-               if (getline(&sline, &slen, sstat) == -1) {
-                       log_system_error_and_thread_exit("cannot read sstat");
-               }
-
-               split(sline, stats, sizeof(stats) / sizeof(stats[0]));
-
-               fseek(pstat, 0, SEEK_SET);
-               fflush(pstat);
-               if (getline(&pline, &plen, pstat) == -1) {
-                       log_system_error_and_thread_exit("cannot read pstat");
-               }
-
-               split(pline, pstats, sizeof(pstats) / sizeof(pstats[0]));
-
-               fseek(pmstat, 0, SEEK_SET);
-               fflush(pmstat);
-               if (fscanf(pmstat, "%ld", &pages) < 1) {
-                       log_system_error_and_thread_exit("fscanf(pmstat) failed");
-               }
-
-               fseek(memf, 0, SEEK_SET);
-               fflush(memf);
-               if (getline(&mtline, &mtlen, memf) == -1) {
-                       log_system_error_and_thread_exit("cannot read memf");
-               }
-               if (getline(&mfline, &mflen, memf) == -1) {
-                       log_system_error_and_thread_exit("cannot read memf");
-               }
-
-               split(mtline, mt, 3);
-               split(mfline, mf, 3);
-
-               struct statvfs fstat;
-               if (statvfs(".", &fstat) < 0) {
-                       log_system_error_and_thread_exit("statvfs");
-               }
-
-               available = (fstat.f_bsize >= 1024)
-                       ? (fstat.f_bsize / 1024) * fstat.f_bavail
-                       : fstat.f_bavail / (1024 / fstat.f_bsize);
-
-               fprintf(stat_file_out,
-                       "%ld.%03d" " %s %s %s" " %s %s" " %s %s %ld %ld\n",
-                       t, millisec,
-                       stats[1], stats[3], stats[4], /* user system idle */
-                       pstats[14 - 1], pstats[15 - 1], /* puser psystem */
-                       mt[1], mf[1], available, pages * psize);
-               if (fflush(stat_file_out) != 0) {
-                       log_system_error_and_thread_exit("fflush");
-               }
-
-               usleep(timeoutMicrosec);
-       }
-       return NULL;
-}
-
 static void CheckValue(char **value, const char *info)
 {
        if (value[0] != NULL) {
-               flog("%s is already defined to %s", info, *value);
+               log_message("%s is already defined to %s", info, *value);
                exit(1);
        }
        value[0] = optarg;
 }
 
-#if TIZEN
-static int output_app_info(const aul_app_info *info, void *data)
-{
-       if (info == NULL || info->appid == NULL)
-               return -1;
-       fprintf(stderr, "pid %d status %d appid %s\n",
-               info->pid, info->status, info->appid);
-       return 0;
-}
-#endif /* TIZEN */
-
-static void ListApps(int n)
-{
-#if TIZEN
-       aul_app_get_all_running_app_info(output_app_info, NULL);
-#endif /* TIZEN */
-}
-
 static int process_option(int argc, char **argv)
 {
        int option_index;
@@ -540,18 +231,20 @@ static int process_option(int argc, char **argv)
                } else if (pname == NULL) {
                        pname = optarg;
                } else {
-                       flog("Extra argument %s", optarg);
+                       log_error("extra argument %s", optarg);
                        exit(1);
                }
                break;
-       case 'a': CheckValue(&appid, "Appid"); break;
+       case 'a': CheckValue(&appid, "appid"); break;
        case 'p': CheckValue(&pname, "pipe name"); break;
        case 'e': CheckValue(&ename, "exe name"); break;
        case 'o': CheckValue(&oname, "error name"); break;
        case 'v': verbose++; break;
-       case 'l': ListApps(0); exit(0);
+#if TIZEN
+       case 'l': tizen_list_running_apps(stderr); exit(0);
+#endif /* TIZEN */
        case 'i': doinfo = 1; break;
-       case 't': timeoutMicrosec = atoi(optarg) * 1000; break;
+       case 't': stat_period_usec = atoi(optarg) * 1000; break;
        case 'w': isPipeOwner = 1; break;
        case 'c': controlPort = atoi(optarg); break;
        case 'd': dataPort = atoi(optarg); break;
@@ -562,75 +255,50 @@ static int process_option(int argc, char **argv)
        return 0;
 }
 
-static void SimpleThread(void* (*func)(void *))
+static pthread_t SimpleThread(void* (*func)(void *), void *arg)
 {
        pthread_attr_t attr;
-       pthread_t thread;
-
-       if (pthread_attr_init(&attr)) {
+       if (pthread_attr_init(&attr) != 0) {
                log_system_error_and_exit("pthread_attr_init");
        }
 
-       if (pthread_create(&thread, &attr, func, NULL)) {
+       pthread_t thread;
+       if (pthread_create(&thread, &attr, func, arg) != 0) {
                log_system_error_and_exit("pthread_create");
        }
-}
-
-#if TIZEN
-static int find_pid(const aul_app_info *info, void *data)
-{
-       if (!strcmp(appid, info->appid)) {
-               *(int*)data = info->pid;
-               return -1;
-       }
-       return 0;
-}
 
-static void waitappid(int n)
-{
-       if (appid == NULL) {
-               exit(1);
-       }
-       for (; n > 0; n--) {
-               if (aul_app_is_running(appid)) {
-                       int npid = 0;
-                       aul_app_get_all_running_app_info(find_pid, &npid);
-                       if (npid) {
-                               pid = npid;
-                               SimpleThread(&wait_thread);
-                               return;
-                       }
-               }
-               sleep(1);
-       }
-       flog("cannot find application %s", appid);
-       exit(1);
+       return thread;
 }
 
+#if TIZEN
 static int app_launch_handler(int npid, const char *app_id, void *data)
 {
        if (verbose) {
-               flog("app_launch_handler %d - %s", npid, app_id);
+               log_message("app_launch_handler (pid=%d; app=%s)", npid, app_id);
        }
 
-       if (pid > 0 || strcmp(app_id, appid))
+       if (app_pid > 0 || strcmp(app_id, appid) != 0) {
                return 0;
+       }
 
-       pid = npid;
+       app_pid = npid;
        return 1;
 }
 
 static int app_dead_handler(int npid, void *data)
 {
        if (verbose) {
-               flog("app_dead_handler %d", npid);
+               log_message("app_dead_handler (pid=%d)", npid);
        }
 
-       if (npid != pid)
+       if (npid != app_pid) {
                return 0;
+       }
 
-       sleep(1);
-       exit(0);
+       if (verbose && !global_stop) {
+               log_message("stop flag set (app_dead_handler)");
+       }
+       global_stop = 1;
        return 1;
 }
 #endif /* TIZEN */
@@ -659,7 +327,7 @@ static int openFileProcess()
        }
 
        if (verbose) {
-               flog("pipefd: [%d, %d]", pipefd[0], pipefd[1]);
+               log_message("pipefd: [%d, %d]", pipefd[0], pipefd[1]);
        }
 
        int id = open(pname, O_RDONLY);
@@ -669,7 +337,7 @@ static int openFileProcess()
        }
 
        if (verbose) {
-               flog("pname id: %d", id);
+               log_message("pname id: %d", id);
        }
 
        int pid = fork();
@@ -681,18 +349,18 @@ static int openFileProcess()
        if (pid == 0) { /* Child */
                close(pipefd[0]);
                if (verbose) {
-                       flog("CHILD close(%d)", pipefd[0]);
+                       log_message("CHILD close(%d)", pipefd[0]);
                }
                dup2(pipefd[1], 1);     /* stdout */
                if (verbose) {
-                       flog("CHILD dup2(%d, 1)", pipefd[1]);
+                       log_message("CHILD dup2(%d, 1)", pipefd[1]);
                }
                dup2(id, 0);            /* stdin */
                if (verbose) {
-                       flog("CHILD dup2(%d, 0)", id);
+                       log_message("CHILD dup2(%d, 0)", id);
                }
                if (verbose) {
-                       flog("CHILD execl(%s)", ename);
+                       log_message("CHILD execl(%s)", ename);
                }
                execl(ename, ename, NULL);
                log_system_error("CHILD execl(%s)", ename); // execl returns only in case of error
@@ -702,12 +370,12 @@ static int openFileProcess()
        /* Parent */
        close(pipefd[1]);
        if (verbose) {
-               flog("PARENT close(%d)", pipefd[1]);
+               log_message("PARENT close(%d)", pipefd[1]);
        }
 
        close(id);
        if (verbose) {
-               flog("PARENT close(%d)", id);
+               log_message("PARENT close(%d)", id);
        }
 
        pipef = fdopen(pipefd[0], "r");
@@ -716,7 +384,7 @@ static int openFileProcess()
                return -1;
        }
        if (verbose) {
-               flog("PARENT fdopen(%d, \"r\")", pipefd[0]);
+               log_message("PARENT fdopen(%d, \"r\")", pipefd[0]);
        }
 
        return 0;
@@ -755,7 +423,7 @@ static int openPort(int port)
        }
 
        if (verbose) {
-               flog("waiting for connection to port %d", port);
+               log_message("waiting for connection to port %d", port);
        }
        struct sockaddr_in caddr;
        int csize = sizeof(caddr);
@@ -766,36 +434,173 @@ static int openPort(int port)
                return -1;
        }
        if (verbose) {
-               flog("accepted connection to port %d", port);
+               log_message("accepted connection to port %d", port);
        }
        close(sock);
        return result;
 }
 
+static int is_command(char *line, char *command, int *arg1, int def_arg1, int *arg2, int def_arg2)
+{
+       *arg1 = def_arg1;
+       if (arg2 != NULL) {
+               *arg2 = def_arg2;
+       }
+       int cmd_len = strlen(command);
+       if (strncmp(line, command, cmd_len) != 0) {
+               return -1;
+       }
+       char *p = line + cmd_len;
+       char *endptr;
+       long l = strtol(p, &endptr, 10);
+       if (endptr != p) {
+               *arg1 = (int)l;
+               if (arg2 != NULL) {
+                       p = endptr;
+                       l = strtol(p, &endptr, 10);
+                       if (endptr != p) {
+                               *arg2 = (int)l;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int send_signal(int target_pid, int signal)
+{
+       if (target_pid == -1) {
+               log_error("cannot send signal %d: no pid", signal);
+               return -1;
+       }
+       int result = kill(target_pid, signal);
+       if (result == 0) {
+               if (verbose) {
+                       log_message("kill(%d, %d) succeeded", target_pid, signal);
+               }
+       }
+       else {
+               log_system_error("kill(%d, %d)", target_pid, signal);
+       }
+       return result;
+}
+
+static void *command_loop_thread(void *arg)
+{
+       /* Read command loop */
+       ssize_t len;
+       char *line = malloc(len = 16);
+       while (!global_stop) {
+               if ((len = getline(&line, &len, ctrl_file_in)) == -1) {
+                       log_system_error("cannot read command");
+                       break;
+               }
+               if (global_stop) {
+                       break;
+               }
+               if (verbose) {
+                       if ((len > 0) && (line[len - 1] == '\n')) {
+                               line[len - 1] = 0;
+                       }
+                       log_message("line: %s", line);
+               }
+               char *reply = line; // echo input line by default
+               int pid;
+               int signal = -1;
+               if (is_command(line, "test", &pid, -1, NULL, 0) == 0) {
+                       if (app_pid == -1) {
+                               if (pid == -1) {
+#if TIZEN
+                                       pid = tizen_find_app_pid(appid, 10); // try 10 seconds
+                                       if (pid == -1) {
+                                               log_error("cannot find application %s", appid);
+                                               reply = "!app_not_found"; // report error
+                                               global_stop = 1;
+                                       }
+#else
+                                       pid = getpid();
+#endif /* TIZEN */
+                               }
+                               app_pid = pid;
+                               if (verbose) {
+                                       log_message("waiting for controlled application exit (pid=%d)", pid);
+                               }
+                               SimpleThread(&wait_app_exited_thread, (void*)(intptr_t)pid);
+                               if (doinfo) {
+                                       if (stat_file_out == NULL) {
+                                               stat_file_out = stdout;
+                                       }
+                                       proc_stat_thread_id = start_write_proc_stat(stat_file_out, pid, stat_period_usec, verbose);
+                                       proc_stat_thread_runs = 1;
+                               }
+                       }
+                       else {
+                               reply = "!app_set_already"; // report error
+                       }
+               }
+               else if (is_command(line, "exit", &pid, app_pid, NULL, 0) == 0) {
+                       send_signal(pid, SIGHUP);
+                       if (verbose && !global_stop) {
+                               log_message("stop flag set (command_loop_thread)");
+                       }
+                       global_stop = 1; // exit from the thread and then the program itself
+               }
+               else if (is_command(line, "stop", &pid, app_pid, NULL, 0) == 0) {
+                       send_signal(pid, SIGRTMIN + 7);
+               }
+               else if (is_command(line, "start", &pid, app_pid, NULL, 0) == 0) {
+                       send_signal(pid, SIGRTMIN + 8);
+               }
+               else {
+                       int arg2;
+                       if (is_command(line, "kill", &pid, app_pid, &arg2, SIGINT) == 0) {
+                               signal = arg2;
+                       }
+               }
+               char *buf = NULL;
+               if (signal >= 0) {
+                       int result = send_signal(pid, signal);
+                       if (asprintf(&buf, "%s : %d", reply, result) != -1) {
+                               reply = buf;
+                       }
+               }
+               if (ctrl_file_out != NULL) {
+                       if (fprintf(ctrl_file_out, "%s\n", reply) < 0 || fflush(ctrl_file_out) != 0) {
+                               log_system_error("cannot write command reply");
+                               fclose(ctrl_file_out);
+                               ctrl_file_out = NULL;
+                       }
+               }
+               if (verbose && reply != line) {
+                       log_error(reply);
+               }
+               free(buf);
+       } // while
+       free(line);
+       command_loop_thread_runs = 0;
+       if (verbose) {
+               log_error("command loop finished");
+       }
+       return NULL;
+}
+
 int main(int argc, char **argv)
 {
        time_t start_time = time(NULL);
 
-       log = stderr;
-
        atexit(close_stdio);
 
-       if (pthread_mutex_init(&log_lock, NULL) != 0) {
-               perror("mutex init failed\n");
+       if (log_init() != 0) {
                return 1;
        }
 
-#if TIZEN
-       struct termios term;
-#endif /* TIZEN */
+       log_set_file(stderr);
 
-       while(!process_option(argc, argv));
+       while (!process_option(argc, argv));
 
        atexit(finish);
-
 #if TIZEN
        if (appid == NULL) {
-               flog("Unknown app id");
+               log_error("Unknown app id");
                exit(1);
        }
 #endif /* TIZEN */
@@ -804,18 +609,20 @@ int main(int argc, char **argv)
                oname = "/tmp/profctl.log";
        }
 
-       log = fopen(oname, "w");
-       if (log == NULL) {
+       FILE *log_file = fopen(oname, "w");
+       if (log_file == NULL) {
                log_system_error_and_exit("freopen");
        }
-       setlinebuf(log);
+       setlinebuf(log_file);
+
+       log_set_file(log_file);
 
        if (verbose) {
-               fprintf(stderr, "=== Started (pid: %d) ===\n", getpid());
+               fprintf(log_file, "=== Started (pid: %d) ===\n", getpid());
                log_time_zone_info(start_time);
                int i;
                for (i = 0; i < argc; i++) {
-                       fprintf(stderr, "argv[%d] = %s\n", i, argv[i]);
+                       fprintf(log_file, "argv[%d] = %s\n", i, argv[i]);
                }
        }
 
@@ -834,6 +641,7 @@ int main(int argc, char **argv)
        }
 
 #if TIZEN
+       struct termios term;
        if (controlPort < 0 && dataPort < 0 && statPort < 0) {
                /* stty */
                tcgetattr(0, &term);
@@ -859,8 +667,9 @@ int main(int argc, char **argv)
                if (ctrl_file_out == NULL) {
                        log_system_error_and_exit("fdopen(control,w)");
                }
-               fprintf(ctrl_file_out, "ready\n");
-               fflush(ctrl_file_out);
+               if (fprintf(ctrl_file_out, "ready\n") < 0 || fflush(ctrl_file_out) != 0) {
+                       log_system_error_and_exit("cannot write 'ready' prompt to control stream");
+               }
        }
        else {
                ctrl_file_in = stdin;
@@ -877,8 +686,10 @@ int main(int argc, char **argv)
                }
        }
 
+       pthread_t data_output_thread_id;
        if (pname) {
-               SimpleThread(&output_thread);
+               data_output_thread_runs = 1;
+               data_output_thread_id = SimpleThread(&data_output_thread, NULL);
        }
 
 #if TIZEN
@@ -886,50 +697,49 @@ int main(int argc, char **argv)
        aul_listen_app_dead_signal(app_dead_handler, NULL);
 #endif /* TIZEN */
 
-       /* Read command loop */
-       ssize_t len;
-       char *line = malloc(len = 16);
-       while (!exiting && (len = getline(&line, &len, ctrl_file_in)) != -1) {
-               if (verbose) {
-                       if ((len > 0) && (line[len - 1] == '\n')) {
-                               line[len - 1] = 0;
-                       }
-                       flog("line: %s", line);
+       command_loop_thread_runs = 1;
+       pthread_t command_loop_thread_id = SimpleThread(&command_loop_thread, NULL);
+
+       struct timespec poll_timeout;
+       poll_timeout.tv_sec = 0;
+       poll_timeout.tv_nsec = 250 * 1000000L; // 250 msec
+       while (!global_stop) {
+               if (command_loop_thread_runs &&
+                       pthread_timedjoin_np(command_loop_thread_id, NULL, &poll_timeout) == 0)
+               {
+                       break; // command loop thread finished
                }
-               if ((pid != -1) && !strncmp(line, "exit", 4)) {
-                       kill(pid, SIGHUP);
+               if (global_stop) {
                        break;
-               } else if ((pid != -1) && !strncmp(line, "kill", 4)) {
-                       if (kill(pid, SIGINT)) {
-                               log_system_error("kill");
-                       }
-               } else if ((pid != -1) && !strncmp(line, "start", 5)) {
-                       if (kill(pid, SIGRTMIN + 8)) {
-                               log_system_error("kill");
-                       }
-               } else if ((pid != -1) && !strncmp(line, "stop", 4)) {
-                       if (kill(pid, SIGRTMIN + 7)) {
-                               log_system_error("kill");
-                       }
-               } else if (!strncmp(line, "test", 4) && (pid == -1)) {
-#if TIZEN
-                       waitappid(10); /* try 10 seconds */
-#else
-                       pid = getpid();
-#endif /* TIZEN */
-                       if (verbose) {
-                               flog("pid = %d", pid);
-                       }
-                       if (doinfo) {
-                               SimpleThread(&outstat_thread); // TODO!! clean termination! (check 'exiting')
-                       }
                }
-               if (ctrl_file_out != NULL) { // echo input line
-                       fprintf(ctrl_file_out, "%s\n", line);
-                       fflush(ctrl_file_out);
+               if (data_output_thread_runs &&
+                       pthread_timedjoin_np(data_output_thread_id, NULL, &poll_timeout) == 0)
+               {
+                       break; // data output thread finished
                }
-       } // while
-       free(line);
+       }
+       if (verbose && !global_stop) {
+               log_message("stop flag set (main)");
+       }
+       global_stop = 1;
+       struct timespec join_timeout;
+       join_timeout.tv_sec = 0;
+       join_timeout.tv_nsec = 500 * 1000000L; // 500 msec
+       if (command_loop_thread_runs) {
+               if (pthread_timedjoin_np(command_loop_thread_id, NULL, &join_timeout) != 0) {
+                       pthread_cancel(command_loop_thread_id);
+               }
+       }
+       if (data_output_thread_runs) {
+               if (pthread_timedjoin_np(data_output_thread_id, NULL, &join_timeout) != 0) {
+                       pthread_cancel(data_output_thread_id);
+               }
+       }
+       if (proc_stat_thread_runs) {
+               if (pthread_timedjoin_np(proc_stat_thread_id, NULL, &join_timeout) != 0) {
+                       pthread_cancel(proc_stat_thread_id);
+               }
+       }
 
        return 0;
 }
diff --git a/tizen_util.c b/tizen_util.c
new file mode 100644 (file)
index 0000000..b0ab0c5
--- /dev/null
@@ -0,0 +1,57 @@
+#if TIZEN
+
+#include <termios.h>
+#include <aul/aul.h>
+
+#include "tizen_util.h"
+
+static int output_app_info(const aul_app_info *info, void *data)
+{
+       if (info == NULL || info->appid == NULL) {
+               return -1;
+       }
+       fprintf((FILE*)data, "pid: %d; status: %d; appid: %s\n",
+               info->pid, info->status, info->appid);
+       return 0;
+}
+
+void tizen_list_running_apps(FILE *file)
+{
+       aul_app_get_all_running_app_info(output_app_info, file);
+}
+
+struct app_info {
+       char *appid;
+       int pid;
+};
+
+static int find_pid(const aul_app_info *info, void *data)
+{
+       struct app_info *ai = (struct app_info*)data;
+       if (strcmp(ai->appid, info->appid) == 0) {
+               ai->pid = info->pid;
+               return -1;
+       }
+       return 0;
+}
+
+int tizen_find_app_pid(char *appid, int timeout_seconds)
+{
+       if (appid == NULL) {
+               return -1;
+       }
+       for (; timeout_seconds > 0; timeout_seconds--) {
+               if (aul_app_is_running(appid)) {
+                       struct app_info ai;
+                       ai.appid = appid;
+                       ai.pid = 0;
+                       aul_app_get_all_running_app_info(find_pid, &ai);
+                       if (ai.pid) {
+                               return ai.pid;
+                       }
+               }
+               sleep(1);
+       }
+       return -1;
+}
+#endif
diff --git a/tizen_util.h b/tizen_util.h
new file mode 100644 (file)
index 0000000..fb8a746
--- /dev/null
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <stdio.h>
+
+void tizen_list_running_apps(FILE *file);
+
+int tizen_find_app_pid(char *appid, int timeout_seconds);