Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Burton2013-04-10 12:57:18 -0400
committerFelix Burton2013-04-11 13:57:10 -0400
commit85fce6099c7721b002d88d14a3e978b4d96d3228 (patch)
tree98fe9c59c5fb3728398945d4c20c366c8d7e3008
parent0c0772c2fb2eeabb179286b5024db482cf793193 (diff)
downloadorg.eclipse.tcf.agent-85fce6099c7721b002d88d14a3e978b4d96d3228.tar.gz
org.eclipse.tcf.agent-85fce6099c7721b002d88d14a3e978b4d96d3228.tar.xz
org.eclipse.tcf.agent-85fce6099c7721b002d88d14a3e978b4d96d3228.zip
Enable initialization output on stdout and stderr in daemon mode and add daemon support on windows
Prior to this change it was not possible to get the output from the -S option in daemon mode. This made it hard for clients to start the TCF agent and know 1) which port is used, 2) when the started agent is ready for incoming connections and 3) if the launch was successful or if it failed.
-rw-r--r--agent/tcf/framework/mdep.c329
-rw-r--r--agent/tcf/framework/mdep.h7
-rw-r--r--agent/tcf/framework/trace.c13
-rw-r--r--agent/tcf/main/main.c28
4 files changed, 362 insertions, 15 deletions
diff --git a/agent/tcf/framework/mdep.c b/agent/tcf/framework/mdep.c
index a6c2a8d3..56a4d9fc 100644
--- a/agent/tcf/framework/mdep.c
+++ b/agent/tcf/framework/mdep.c
@@ -1199,7 +1199,202 @@ const char * loc_gai_strerror(int ecode) {
#endif
-#if defined(_WIN32) || defined(_WRS_KERNEL) || defined (__SYMBIAN32__)
+#if defined(_WIN32)
+# include <tlhelp32.h>
+# ifdef _MSC_VER
+# pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union (in winternl.h) */
+# include <winternl.h>
+# else
+# include <ntdef.h>
+# endif
+# ifndef STATUS_INFO_LENGTH_MISMATCH
+# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+# endif
+# ifndef SystemHandleInformation
+# define SystemHandleInformation 16
+# endif
+
+/* Disable inheritance for all handles owned by process */
+static NTSTATUS disable_handle_inheritance(void) {
+ typedef struct _HANDLE_INFORMATION {
+ USHORT ProcessId;
+ USHORT CreatorBackTraceIndex;
+ UCHAR ObjectTypeNumber;
+ UCHAR Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+ } HANDLE_INFORMATION;
+ typedef struct _SYSTEM_HANDLE_INFORMATION {
+ ULONG Count;
+ HANDLE_INFORMATION Handles[1];
+ } SYSTEM_HANDLE_INFORMATION;
+ typedef NTSTATUS (FAR WINAPI * QuerySystemInformationTypedef)(int, PVOID, ULONG, PULONG);
+ QuerySystemInformationTypedef QuerySystemInformationProc = (QuerySystemInformationTypedef)GetProcAddress(
+ GetModuleHandle("NTDLL.DLL"), "NtQuerySystemInformation");
+ DWORD size;
+ NTSTATUS status;
+ SYSTEM_HANDLE_INFORMATION * hi = NULL;
+
+ size = sizeof(SYSTEM_HANDLE_INFORMATION) + sizeof(HANDLE_INFORMATION) * 256;
+ hi = (SYSTEM_HANDLE_INFORMATION *)tmp_alloc_zero(size);
+ for (;;) {
+ status = QuerySystemInformationProc(SystemHandleInformation, hi, size, &size);
+ if (status != STATUS_INFO_LENGTH_MISMATCH) break;
+ hi = (SYSTEM_HANDLE_INFORMATION *)tmp_realloc(hi, size);
+ }
+ if (status == 0) {
+ ULONG i;
+ DWORD id = GetCurrentProcessId();
+ for (i = 0; i < hi->Count; i++) {
+ if (hi->Handles[i].ProcessId != id) continue;
+ SetHandleInformation((HANDLE)(uintptr_t)hi->Handles[i].Handle, HANDLE_FLAG_INHERIT, 0);
+ }
+ }
+ return status;
+}
+
+static char *make_cmd_from_args(char **args) {
+ int i = 0;
+ int cmd_size = 0;
+ int cmd_pos = 0;
+ char *cmd = NULL;
+
+# define cmd_append(ch) { \
+ if (cmd_pos >= cmd_size) { \
+ cmd_size += 0x1000; \
+ cmd = (char *)loc_realloc(cmd, cmd_size); \
+ } \
+ cmd[cmd_pos++] = (ch); \
+ }
+ while (args[i] != NULL) {
+ const char * p = args[i++];
+ if (cmd_pos > 0) cmd_append(' ');
+ cmd_append('"');
+ while (*p) {
+ if (*p == '"') cmd_append('\\');
+ cmd_append(*p);
+ p++;
+ }
+ cmd_append('"');
+ }
+ cmd_append(0);
+# undef cmd_append
+ return cmd;
+}
+
+static int running_as_daemon = 0;
+
+int is_daemon(void) {
+ return running_as_daemon;
+}
+
+# if !defined(__CYGWIN__)
+# define pipe(fds) _pipe((fds), 1024, 0)
+# endif
+
+void become_daemon(char **args) {
+ int fdpairs[4];
+ int npairs = 2;
+ char fnm[FILE_PATH_SIZE];
+ STARTUPINFO startupInfo;
+ PROCESS_INFORMATION prs_info;
+ int i;
+
+ assert(!running_as_daemon);
+ running_as_daemon = 1;
+
+ if (args == NULL)
+ return;
+
+ fflush(stdout);
+ fflush(stderr);
+
+ /* Make sure no handles are inherited by new process */
+ disable_handle_inheritance();
+
+ if (pipe(fdpairs) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+
+ if (pipe(fdpairs + 2) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+
+ if (GetModuleFileName(NULL, fnm, sizeof(fnm)) == 0) {
+ fprintf(stderr, "GetModuleFileName failed\n");
+ exit(1);
+ }
+
+ memset(&startupInfo, 0, sizeof startupInfo);
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ startupInfo.hStdInput = INVALID_HANDLE_VALUE;
+ startupInfo.hStdOutput = (HANDLE)_get_osfhandle(fdpairs[1]);
+ startupInfo.hStdError = (HANDLE)_get_osfhandle(fdpairs[3]);
+ SetHandleInformation(startupInfo.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+ SetHandleInformation(startupInfo.hStdError, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+
+ if (!CreateProcess(fnm, make_cmd_from_args(args), NULL, NULL, TRUE,
+ DETACHED_PROCESS, NULL, NULL, &startupInfo, &prs_info)) {
+ fprintf(stderr, "CreateProcess failed 0x%lx\n", GetLastError());
+ exit(1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* Make read side non-blocking */
+ DWORD pipemode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
+ if (!SetNamedPipeHandleState((HANDLE)_get_osfhandle(fdpairs[i*2]), &pipemode, NULL, NULL)) {
+ fprintf(stderr, "SetNamedPipeHandleState failed 0x%lx\n", GetLastError());
+ exit(1);
+ }
+
+ /* Close write side of pipes so we get end of file as soon as
+ * the new them or exits */
+ close(fdpairs[i*2 + 1]);
+ fdpairs[i*2 + 1] = 1;
+ }
+
+ while (npairs > 0) {
+ for (i = 0; i < npairs; i++) {
+ char tmpbuf[1000];
+ DWORD size;
+ if (ReadFile((HANDLE)_get_osfhandle(fdpairs[i*2]), tmpbuf, sizeof tmpbuf, &size, NULL)) {
+ if (write(fdpairs[i*2 + 1], tmpbuf, size) < 0)
+ perror("write");
+ } else if (GetLastError() != ERROR_NO_DATA) {
+ if (GetLastError() != ERROR_BROKEN_PIPE) {
+ fprintf(stderr, "SetNamedPipeHandleState failed 0x%lx\n", GetLastError());
+ }
+ fdpairs[i*2] = fdpairs[(npairs - 1)*2];
+ fdpairs[i*2 + 1] = fdpairs[(npairs - 1)*2 + 1];
+ npairs--;
+ }
+ }
+ /* Neither select nor overlapped I/O works for anonymous pipes, so use
+ * polling for now until a better solution if found... */
+ usleep(1000);
+ }
+
+ exit(0);
+}
+
+void close_out_and_err(void) {
+ int fd = open("nul", O_WRONLY, 0);
+ if (fd < 0) {
+ perror("open nul");
+ exit(1);
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ dup2(fd, 1);
+ dup2(fd, 2);
+}
+
+#elif defined(_WRS_KERNEL) || defined (__SYMBIAN32__)
int is_daemon(void) {
return 0;
@@ -1210,9 +1405,10 @@ void become_daemon(void) {
exit(1);
}
-#else
+void close_out_and_err(void) {
+}
-#include <syslog.h>
+#else
static int running_as_daemon = 0;
@@ -1221,13 +1417,132 @@ int is_daemon(void) {
}
void become_daemon(void) {
+ int fdpairs[4];
+ int npairs = 2;
+
assert(!running_as_daemon);
- openlog("tcf-agent", LOG_PID, LOG_DAEMON);
- if (daemon(0, 0) < 0) {
- syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "Cannot become a daemon: %m");
+
+ fflush(stdout);
+ fflush(stderr);
+
+ if (pipe(fdpairs) < 0) {
+ perror("pipe");
exit(1);
}
- running_as_daemon = 1;
+
+ if (pipe(fdpairs + 2) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+
+ /* Fork a new process so we can close everything except the pipes */
+ switch (fork()) {
+ default: /* Parent process */
+ /* Close write side of pipes so we get end of file as soon as
+ * child closes them or exits */
+ close(fdpairs[1]);
+ close(fdpairs[3]);
+ fdpairs[1] = 1;
+ fdpairs[3] = 2;
+
+ while (npairs > 0) {
+ int i;
+ int rval;
+ int nfds = 0;
+ fd_set readfds;
+ char tmpbuf[1000];
+
+ FD_ZERO(&readfds);
+ for (i = 0; i < npairs; i++) {
+ int fd = fdpairs[i*2];
+ FD_SET(fd, &readfds);
+ if (nfds < fd)
+ nfds = fd;
+ }
+ rval = select(nfds + 1, &readfds, NULL, NULL, NULL);
+ if (rval < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("select");
+ _exit(1);
+ }
+ assert(rval > 0);
+ for (i = 0; i < npairs; i++) {
+ int fd = fdpairs[i*2];
+ if (FD_ISSET(fd, &readfds)) {
+ rval = read(fd, tmpbuf, sizeof tmpbuf);
+ if (rval > 0) {
+ if (write(fdpairs[i*2 + 1], tmpbuf, rval) < 0)
+ perror("write");
+ } else {
+ if (rval < 0)
+ perror("read");
+ fdpairs[i*2] = fdpairs[(npairs - 1)*2];
+ fdpairs[i*2 + 1] = fdpairs[(npairs - 1)*2 + 1];
+ npairs--;
+ }
+ }
+ }
+ }
+ _exit(0);
+ break;
+
+ case 0: { /* Child process */
+ /* Replace stdin with /dev/null */
+ int fd = open("/dev/null", O_RDONLY);
+ if (fd < 0) {
+ perror("open /dev/null");
+ exit(1);
+ }
+ dup2(fd, 0);
+ dup2(fdpairs[1], 1);
+ dup2(fdpairs[3], 2);
+
+ /* Close all open files except stdin, stdout and stderr */
+ fd = sysconf(_SC_OPEN_MAX);
+ while (fd-- > 3)
+ close(fd);
+
+ /* Fork a new process so it is owned by init */
+ switch (fork()) {
+ default: /* Parent process */
+ _exit(0);
+ break;
+
+ case 0: /* Child process */
+ /* Create new session */
+ if (setsid() == (pid_t)-1) {
+ perror("setsid");
+ _exit(1);
+ }
+
+ running_as_daemon = 1;
+ return;
+
+ case -1: /* Fork failed */
+ perror("fork");
+ exit(1);
+ }
+ }
+
+ case -1: /* Fork failed */
+ perror("fork");
+ exit(1);
+ }
+}
+
+void close_out_and_err(void) {
+ int fd = open("/dev/null", O_WRONLY);
+ if (fd < 0) {
+ perror("open /dev/null");
+ exit(1);
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ dup2(fd, 1);
+ dup2(fd, 2);
}
#endif
diff --git a/agent/tcf/framework/mdep.h b/agent/tcf/framework/mdep.h
index 3c25673f..e449a595 100644
--- a/agent/tcf/framework/mdep.h
+++ b/agent/tcf/framework/mdep.h
@@ -397,7 +397,14 @@ extern const char * get_user_name(void);
extern const char * create_uuid(void);
/* Switch to running in the background, rather than under the direct control of a user */
+#if defined(_WIN32)
+extern void become_daemon(char ** argv);
+#else
extern void become_daemon(void);
+#endif
+
+/* Close stdout and stderr and replace with null output device */
+extern void close_out_and_err(void);
/* Return 1 if running in the background, return 0 othewise */
extern int is_daemon(void);
diff --git a/agent/tcf/framework/trace.c b/agent/tcf/framework/trace.c
index e9095495..1f113a44 100644
--- a/agent/tcf/framework/trace.c
+++ b/agent/tcf/framework/trace.c
@@ -34,6 +34,8 @@ int log_mode = LOG_EVENTS | LOG_CHILD | LOG_WAITPID | LOG_CONTEXT | LOG_PROTOCOL
#include <syslog.h>
#endif
+static int use_syslog = 0;
+
FILE * log_file = NULL;
#define MAX_TRACE_MODES 40
@@ -67,7 +69,7 @@ int print_trace(int mode, const char * fmt, ...) {
if (mode != LOG_ALWAYS && (log_mode & mode) == 0) return 0;
va_start(ap, fmt);
- if (is_daemon()) {
+ if (use_syslog) {
#if defined(_WIN32)
#elif defined(_WRS_KERNEL)
#elif defined(__SYMBIAN32__)
@@ -175,6 +177,15 @@ void open_log_file(const char * log_name) {
}
else if (strcmp(log_name, LOG_NAME_STDERR) == 0) {
log_file = stderr;
+ if (is_daemon()) {
+#if defined(_WIN32)
+#elif defined(_WRS_KERNEL)
+#elif defined(__SYMBIAN32__)
+#else
+ use_syslog = 1;
+ openlog("tcf-agent", LOG_PID, LOG_DAEMON);
+#endif
+ }
}
else if ((log_file = fopen(log_name, "a")) == NULL) {
fprintf(stderr, "TCF: error: cannot create log file %s\n", log_name);
diff --git a/agent/tcf/main/main.c b/agent/tcf/main/main.c
index e3a56619..c43bf3b9 100644
--- a/agent/tcf/main/main.c
+++ b/agent/tcf/main/main.c
@@ -195,7 +195,7 @@ int main(int argc, char ** argv) {
/* Parse arguments */
for (ind = 1; ind < argc; ind++) {
- const char * s = argv[ind];
+ char * s = argv[ind];
if (*s != '-') {
break;
}
@@ -214,6 +214,17 @@ int main(int argc, char ** argv) {
break;
case 'd':
+#if defined(_WIN32)
+ /* For Windows the only way to detach a process is to
+ * create a new process, so we patch the -d option to
+ * -D for the second time we get invoked so we don't
+ * keep on creating new processes forever. */
+ s[-1] = 'D';
+ daemon = 2;
+ break;
+
+ case 'D':
+#endif
daemon = 1;
break;
@@ -279,13 +290,13 @@ int main(int argc, char ** argv) {
}
}
- if (daemon && log_name != NULL && strcmp (log_name, LOG_NAME_STDERR) != 0) {
- fprintf(stderr, "%s: error: can only log to stderr when in daemon "
- "mode.\n", progname);
- exit (1);
+ if (daemon) {
+#if defined(_WIN32)
+ become_daemon(daemon > 1 ? argv : NULL);
+#else
+ become_daemon();
+#endif
}
-
- if (daemon) become_daemon();
open_log_file(log_name);
#endif
@@ -329,6 +340,9 @@ int main(int argc, char ** argv) {
loc_free(server_properties);
}
+ if (daemon)
+ close_out_and_err();
+
#if ENABLE_SignalHandlers
signal(SIGABRT, signal_handler);
signal(SIGILL, signal_handler);

Back to the top