--- /dev/null
+#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;
+}
#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;
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'},
{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;
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?
static void finish()
{
- flog("exit handler called");
+ log_message("exit handler called");
#if TIZEN
if (controlPort < 0) {
tcsetattr(0, TCSANOW, &sterm);
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;
} 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;
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 */
}
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);
}
if (verbose) {
- flog("pname id: %d", id);
+ log_message("pname id: %d", id);
}
int pid = fork();
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
/* 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");
return -1;
}
if (verbose) {
- flog("PARENT fdopen(%d, \"r\")", pipefd[0]);
+ log_message("PARENT fdopen(%d, \"r\")", pipefd[0]);
}
return 0;
}
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);
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 */
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]);
}
}
}
#if TIZEN
+ struct termios term;
if (controlPort < 0 && dataPort < 0 && statPort < 0) {
/* stty */
tcgetattr(0, &term);
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;
}
}
+ 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
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;
}